JS - Alternative to Pointer Events

Hi there,

So I’ve been putting together a simple memory card game and want to ensure the user can’t click on the cards being matched while they’re being checked. There is a deliberate delay in the “matching” so that the user can see that they have indeed matched or not.

I’ve found that I can use the style “pointer-events” but this isn’t supported in Safari or IE. The idea initially was that “pointer-events” would be set to “none” on the elements being checked, and then “pointer-events” was set to “auto” if it was found the cards didn’t match.

Therefore, what alternatives do I have to ensure that the user can’t click on the card elements while they’re being compared/viewed?

Below is the JS I have so far…

//document.getElementById("start-game").onclick(startGame());

establishElements();
buildGame();

function establishElements(){
    gameBoard = document.getElementById("game-board");

    timerMinutes = document.getElementById("minutes");
    timerSeconds = document.getElementById("seconds");
    
    allCards = document.getElementsByClassName("card");

    gameStartContainer = document.getElementById("game-start-container");

    gameEndContainer = document.getElementById("game-end-container");
    gameEndInner =  document.getElementById("game-end__inner");
    gameEndTitle = document.getElementById("game-end-title");
    gameEndMessage = document.getElementById("game-end-message");
}

function startGame(){
    gameStartContainer.classList.add('animated', 'slideOutUp');

    startTimer();
}

function restartGame(){
    
    Element.prototype.remove = function() {
        this.parentElement.removeChild(this);
    }

    NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
        for(var i = this.length - 1; i >= 0; i--) {
            if(this[i] && this[i].parentElement) {
                this[i].parentElement.removeChild(this[i]);
            }
        }
    }

    allCards.remove();

    buildGame();

    gameEndContainer.classList.remove('slideInDown');
    gameEndContainer.classList.add('animated', 'slideOutUp');

    gameEndInner.classList.remove('fadeIn', 'delay-1s');

    gameEndTitle.classList.remove('bounceIn','delay-1s');

    gameStartContainer.classList.add('animated', 'slideOutUp');

    startTimer();
}

function startTimer() {
    resetTimer();
    
    function pad(val) {
        return val > 9 ? val : "0" + val;
    }

    timer = setInterval(function () {
        timerSeconds.innerText = pad(++sec % 60);

        if (sec > 59) {
            timerMinutes.innerText = pad(parseInt(sec / 60));
        }

    //*************
    // TESTING
    //*************
    //// Call a combination of the following to show game end scenarios instantly
    // turn = 20; // normal amount of turns
    // turn = 40; // too many turns
    // document.getElementById("minutes").innerText = 60 // too much time
    // gameEnd(); // game end

        if (timerMinutes.innerText >= "60") {
            timerSeconds.innerText = "00";
            gameEnd();
        }

    }, 1000);
}

function resetTimer(){
    timerMinutes.innerText = "00";
    timerSeconds.innerText = "00";
    turn = 0;
    sec = 0 ;
}

function buildGame() {
 
    gameWidth = 2;
    gameHeight = 2;
    firstCard = null;
    secondCard = null;

    cardImageURL = "....";

    turn = 0;
    matches = 0;
    checkTimeout = null;

    sec = 0;

    turnUpdate = function () {
        turnDisplay = document.getElementsByClassName("turn-display");  // Find the elements
        for (var i = 0; i < turnDisplay.length; i++) {
            turnDisplay[i].innerText = turn;    // Change the content
        }
    }

    createGrid = function (h, v) {
        var a = [];

        // create squares within the grid and push the values into an array
        for (var i = 0; i < gameWidth * gameHeight / 2; i++) {
            a.push(i);
            a.push(i);
        }

        // create a randomised array of numbers using the contents of the "a" array created above
        // remove this value from the "a" array ready for the next number
        var s = [];
        while (a.length > 0) {
            var r = Math.floor(Math.random() * a.length);
            s.push(a[r]);
            a.splice(r, 1);
        }

        // create the grid using the "s" array
        for (var x = 0; x < h; x++) {
            for (var y = 0; y < v; y++) {
                createCard(s.pop(), x, y);
            }
        }
    }

    createCard = function (cardNum, posX, posY) {
        var card = document.createElement("img");
        card.num = cardNum;
        card.src = cardImageURL + "xmas18_memorygame_tileback.jpg";
        card.classList.add("card");
        card.onclick = clickCard;
        gameBoard.appendChild(card);
    }

    clickCard = function (e) {
        var card = e.target;

        // remove the card animations
        card.classList.remove("animated");
        card.classList.remove("rubberBand");
        card.classList.remove("fast");


        // if another element is clicked while other cards are being checked 
        // then clear the timeout and run checkCards again
        if (checkTimeout != null) {
            clearTimeout(checkTimeout);
            checkCards();
        }

        // set card image for each card
        card.src = cardImageURL + "xmas18_memorygame_tile" + card.num + ".jpg";

        // check if firstCard or secondCard are set to null
        if (firstCard == null) {
            firstCard = card;
        // if firstCard is clicked
        } else if (firstCard == card) {
            firstCard.src = cardImageURL + "xmas18_memorygame_tileback.jpg";
            firstCard = null;
            turn++;
            turnUpdate();
        // if secondCard is clicked
        } else if (secondCard == null) {
            secondCard = card;
            firstCard.style.pointerEvents = "none";
            secondCard.style.pointerEvents = "none";
            checkTimeout = setTimeout(checkCards, 500);
        }
    }

    checkCards = function () {
        // if the num of firstCard and secondCard match then remove from board
        if (firstCard.num == secondCard.num) {
            firstCard.style.visibility = "hidden";
            secondCard.style.visibility = "hidden";
            
            //firstCard.remove();
            //secondCard.remove();

            matches++;

            // if game complete
            if (matches >= gameWidth * gameHeight / 2) {
                gameEnd();
            }

        } else {
            // animate the first and second cards
            firstCard.classList.add('animated','rubberBand','fast');
            secondCard.classList.add('animated','rubberBand','fast');

            // if the cards don't match then set their src back to the original and pointer events to auto
            firstCard.src = cardImageURL + "xmas18_memorygame_tileback.jpg";
            secondCard.src = cardImageURL + "xmas18_memorygame_tileback.jpg";
            firstCard.style.pointerEvents = "auto";
            secondCard.style.pointerEvents = "auto";
        }

        // add 1 to number of turns
        turn++;
        turnUpdate();

        // reset variables back to null so the next turn can be taken
        firstCard = null;
        secondCard = null;
        checkTimeout = null;
    }

    gameEnd = function () {
        // remove pre-existing animations
        gameEndContainer.classList.remove("slideOutUp"); 

        // set game end container and inner elements to visible/animate
        gameEndContainer.style.visibility = "visible";
        gameEndContainer.style.display = "flex";
        gameEndContainer.classList.add('animated', 'slideInDown');

        gameEndInner.classList.add('animated', 'fadeIn', 'delay-1s');

        gameEndTitle.classList.add('animated', 'bounceIn','delay-1s');

        turnUpdate();
        clearInterval(timer);

        upperPrizeMsg = "...and wow! You completed it in " + document.getElementById("minutes").innerText + " minute(s) " + document.getElementById("seconds").innerText + " seconds! <br /> Here's your prize....";
        lowerPrizeMsg = "Hmm, seems like you need some more practice. <br /> Here's a small prize to keep you going....";
        outOfTimeMsg = "Oh dear, you ran out of time!";
        sorryMsg = "Sorry, you didn't win a discount code this time. Try again!";

        if (turn >= 0 && turn < 32 && document.getElementById("minutes").innerText < "05") {
            gameEndMessage.innerHTML = upperPrizeMsg;
        } else if (turn >= 32) {
            gameEndMessage.innerHTML = lowerPrizeMsg;
        } else if (document.getElementById("minutes").innerText >= "60") {
            gameEndMessage.innerHTML = outOfTimeMsg
        } else {
            gameEndMessage.innerHTML = sorryMsg;
        }
    }

    turnUpdate();
    createGrid(gameWidth, gameHeight);

}

Many thanks in advance!

Apologies, it looks like I was getting confused with “Pointer Events” - the CSS property “pointer-events” is accepted on all “current” browsers except Opera Mini. Internet Explorer 6-10 are the only older, major browser versions which do not accept this property.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.