locked
Event listener question

    Question

  • I am developing a memory game: the user clicks a card that is face down. This click reveals the picture. If two cards have been clicked the cards are compared to see if they match. If they do not match the cards turn over again. There are several game levels with different numbers of cards.

    The problem I have is that if several cards are clicked in very quick succession, then up to 4 cards can be viewed at once, and the game play is then unpredictable.

    I have read about event listeners and I do not think that stopImmediatePropagation is the answer.

    The event listener is added dynamically.

                for (k = 0; k < newpic.length; k++) {
    
                    image = document.createElement('img');
                   
                    image.src = newpic[k];
                    image.id = k;
                    image.value = "false";
                   
                    outerdeck.appendChild(image);
                                  document.getElementById(k).setAttribute("value", "false");
                                    var cardshow = document.getElementById(k);
                    cardshow.addEventListener("click", show.bind({ id: k }), false);
    
    etc
    }

    The "k" is important. The cards have been shuffled. The card is identifiable from newpic[k].

    When someone clicks on a card the value of k is passed to show() so that it can be revealed.

    The beginning of this function is:

      function show(k) 
            {
                          var front = document.getElementById(this.id);
                          if (front.getAttribute("value") === "false"  && turn < 2 && front.getAttribute("state") === "notmatched" ) {
    
                               front.setAttribute("value", "true");
                              
                    turn++;
                    front.src = newpic[this.id];
        

    The condition turn < 2 should prevent the card being revealed if more than 2 have been turned.

    This works if cards are clicked slowly, but not if they are clicked very quickly in succession.

    Is this because of asynchronous running of the functions?

    How do I deal with this bug in my app please? I do not know what to try next.

    Many thanks for your help.

    Thursday, May 22, 2014 8:12 AM

Answers

  • Many thanks.

    Have now solved it.

    The problem was not how quickly I increased turn, but it was when I returned it to zero again. I had a setTimeout to turn cards back over and reset values after the two cards had been compared. I then made turn = 0 again. I thought erronously that the delay in setTimeout would delay things enough. But of course it works asynchronously so the turn = 0 came far too soon. In fact it enabled me to start turning cards again before the first pair had been turned back.

    Have now remedied this by putting the turn = 0 into a setTimeout.

    Had not come across pointerdown - will definitely look into this.

    Thank you (and also to Mike Jones) for your reply. Hopefully I will get the hang of this eventually. It is proving trickier than I thought to turn a browser javascript game into a working app. I will get there though...

    • Marked as answer by chickfeed Thursday, May 22, 2014 7:17 PM
    Thursday, May 22, 2014 7:16 PM

All replies

  • I'm not sure how some of the "front.*" attributes are intended to work in your code, so that is a caveat, but perhaps you need to make sure that "turn" gets incremented every time show() runs, whether or not the conditional statement resolves as true. Possibly something like this:

    function show(k) {

        var front = document.getElementById(this.id);
        if (front.getAttribute("value") === "false" 
            && turn < 2 && front.getAttribute("state") === "notmatched" ) {

                front.setAttribute("value", "true");
                front.src = newpic[this.id];
        }
        turn++;
    }

    Thursday, May 22, 2014 3:26 PM
  • I would increment turn immediately inside show(), for one thing, and compare to turn <= 2. Also, you might be better off creating more discrete objects for each card that you use with show.bind, so that you can store the value and state directly as properties rather than using get/setAttribute (that is, avoid DOM API calls). Also, I assume your newpic array is already pre-populated with pre-loaded card images, so that the front.src assignment happens very quickly.

    Also, try using "pointerDown" instead of "click" as your event. In my experience (e.g. with my 15+Puzzle app in the Store that is very responsive), pointerDown responds much more quickly because it's the most direct event. click works but it's generated automatically in response to pointerDown+pointerUp and thus goes through some other time-consuming layers.

    Kraig

    Author, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, a free ebook from Microsoft Press.

    Thursday, May 22, 2014 4:09 PM
  • Many thanks. Had tried this and this had worked when it was just a browser game.

    Have now solved it - see below.

    Thursday, May 22, 2014 7:03 PM
  • Many thanks.

    Have now solved it.

    The problem was not how quickly I increased turn, but it was when I returned it to zero again. I had a setTimeout to turn cards back over and reset values after the two cards had been compared. I then made turn = 0 again. I thought erronously that the delay in setTimeout would delay things enough. But of course it works asynchronously so the turn = 0 came far too soon. In fact it enabled me to start turning cards again before the first pair had been turned back.

    Have now remedied this by putting the turn = 0 into a setTimeout.

    Had not come across pointerdown - will definitely look into this.

    Thank you (and also to Mike Jones) for your reply. Hopefully I will get the hang of this eventually. It is proving trickier than I thought to turn a browser javascript game into a working app. I will get there though...

    • Marked as answer by chickfeed Thursday, May 22, 2014 7:17 PM
    Thursday, May 22, 2014 7:16 PM