My homework after completing javascript30 course day 1 lesson: JavaScript Pair Match game

About one month ago I was trying this course: javascript30. After first day lesson, I followed the Wes Bos advice (creator of the javascript30 challenge), building things. What things?

In this post, I'm going to share you my "homework". We're going to make JavaScript Pair Match game.

It is a memory game where you need to match pairs of tiles. Playing is very simple: you turn over one tile and then try to find a matching tile. When you click on the first tile of a turn, you should be able to make a match (if you have exposed a matching tile in a previous turn, and you have perfect recall, or obviously, by serendipity).

Following the javascript30 course approach, no frameworks, no compilers, no libraries, no boilerplate will be used, only vanilla JavaScript coding (a base of html and css is assumed).

The html structure is very simply, a bunch of div's each one with an image element inside.

<div tileID="4" class="tile"> <img picID="4" class="pic" /></div>

Every tile (div) has got a non standard attribute ("tileID"), with a number. And each inner image has got another one ("picID"). Tiles are numbered from 1 to 12, and imgages are numbered from 1 to 6 (each img appears twice, obviously).

After page loading, the initializePanel function is executed.

initializePanel = function() {
    var sourcePictures = ["images/grapes.jpg", 
                          "images/cherries.jpg", 
                          "images/bananas.jpg",
                          "images/apricots.jpg",
                          "images/apples.jpg",
                          "images/akaiBerries.jpg"
                         ];     
    var nextRandomFromList = randomFromListGenerator 
        ([1,2,3,4,5,6,7,8,9,10,11,12]);
    sourcePictures.forEach(function(source, index) {
        var posA = nextRandomFromList();
        initializeImage(index, source, posA);
        var posB = nextRandomFromList();
        initializeImage(index, source, posB);
    });   
}

sourcePictures is the images path array. nextRandomFromList is a random numbers generator (from 1 to 12). It's used to positioning images into tiles randomly. For each image, initializeImage function is called twice (remember, each image is placed into two positions).

What does this function do?

initializeImage = function(picIndex, source, tileNumber) {
    var img = 
        document.querySelector('img[picID="'+tileNumber+'"]'); 
    img.src = 'images/questionMark.jpg';
    img.source = source;        
    var tile = document.querySelector(
        'div[tileID="' + tileNumber + '"]');
    tile.imageID = picIndex;
    tile.addEventListener('click', function(e) {
    selectTile(e, picIndex, tileNumber);
    });
}

It initially stablishes the src attribute of the image to 'images/questionMark.jpg', and set a non standard attribute (source attribute) with the fruit image path. After that, the function adds an event listener for the click event to the tile (div), ensuring that selectTile funcion will be called when player click on a tile.

What will happen when user click on a tile?

Mainly, the class "selected" is added to the tile (div).

var tile = document.querySelector('div[tileID="' + tileNumber + '"]');
tile.classList.add('selected')

And also the questionMark image is replaced by the fruit one. Remember, the fruit image path was stored in a data (non standard) attribute ("source" attribute).

var img = document.querySelector('img[picID="' + tileNumber + '"]');
img.src = img.source;

Finally, some lines of code to find out if a pair is matched or not, etc...

A lot of improvements can be done, such as levels of expertise, each getting progressively harder, or a scoring system, for example. Technically, there should be a more objetual approach: you will note the absence of a board object, a tile object, and so on.

The more important things we learn from this exercise are:

1) The massive use of the HTML DOM querySelector() method (combined with the use of data (non standard) attributes). Its important the way you set data (custom) attributes to DOM elements, when you use querySelector to select this elements.

We've discovered a very fundamental difference between the 2 methods of setting custom DOM attributes in Javascript.

<img id="i4" class="pic" />

<script>
    document.getElementById('i4').picID = '4';
    // FAILS: returns null
    console.log( document.querySelector('*[picID]') ); 

    document.getElementById('i4').setAttribute('picID','4');
    // WORKS: returns element
    console.log( document.querySelector('*[picID]') ); 
</script>

So basically if you apply attributes to an element without using .setAttribute() method then you cannot later select the element by the attribute name.

2) The use of a closure to build your own random numbers generator, based on a list of numbers.

You can download sources from my git repository.

Maybe you can suggest us some improvements, or simply code a new version of this exercise... If you decide to share it with us, you'll be very welcome, we're looking forward to hearing from you.

Thank you very much and see you in the next one.