Search code examples
javascriptbuttonforeach

Accessing Javascript button elements created with a forEach function (not HTML buttons)


I'm trying to make a NYT connections game, which requires 16 buttons. I created them using a forEach function, but only know how to access each button when I click on it. I want to disable all 4 choices when they are correct. How do I access the 4 buttons that the user chose using the button.group value? :

const parentElement = document.querySelector('.game-container');
    wordButtons.forEach (function (b) {
        const button = document.createElement('button');
        button.innerText = b.word;
        button.group = b.group;
        button.id = b.buttonId;
        button.active = false;
        parentElement.appendChild(button); 

            button.onclick = function(){
                if (button.active === false){
                    //case if you click it and choose it
                    buttonIDs.push(button.id);
                    buttonGroups.push(button.group);
                    button.active = true;
                    button.style.backgroundColor = "white";
                } else {
                    //case if you un-choose it
                    button.active = false;
                    button.style.backgroundColor = "rgb(241, 215, 225)";
                    // remove button id from array
                    buttonIDs.splice(buttonIDs.indexOf(button.id));
                    buttonGroups.splice(buttonGroups.indexOf(button.group),1);
                }
         
                if (buttonIDs.length === 4){
                    if (buttonGroups.every(x => x === buttonGroups[0])){
                        console.log("you matched a set!");
                        if (button.group === buttonGroups[0]){
                            button.disabled = true;
                        };
                    }
                }
                }
            })

Sorry if I put too much code here- it is my first time asking a question here.

I tried removing the 'if buttonIds length === 4' case out of the execution context of the onclick function, hoping to access all of the buttons that way and disable them, but if I remove it from the onclick function, the code block does not execute at all.

I could probably create the buttons individually and give them numbers, but that seems like bad form.


Solution

  • I'm not 100% into the game play... In the following example I went with "check state on change".

    Instead of using button elements use checkboxes. A checkbox have a state -- checked or not, and can be disabled. I use both properties to decide if all in one group is checked and after that disabling all checkboxes in a group. Checkboxes are also already grouped by the name they are given in a form.

    Here, I'm just going with 2 words in one group...

    const words = [{
      word: 'Word1',
      group: 1
    }, {
      word: 'Word2',
      group: 1
    }, {
      word: 'Word3',
      group: 2
    }, {
      word: 'Word4',
      group: 2
    }];
    
    words.forEach(word => {
      let label = document.createElement('label');
      let span = document.createElement('span');
      let input = document.createElement('input');
      input.name = `group_${word.group}`;
      input.groupid = word.group;
      input.type = 'checkbox';
      span.textContent = word.word;
      label.appendChild(input);
      label.appendChild(span);
      document.forms.game.appendChild(label);
    });
    
    document.forms.game.addEventListener('change', e => {
      let form = e.target.form;
      let checked = [...form.elements].filter(input => input.checked && !input.disabled);
      if (checked.length == 2) {
        checkGroup(e.target.groupid);
      }
      if (checked.length > 2) {
        e.target.checked = false;
      }
    });
    
    function checkGroup(groupid) {
      let form = document.forms.game;
      let inputs = form[`group_${groupid}`];
      let checked = [...inputs].filter(input => input.checked);
      if (checked.length == 2) {
        [...inputs].forEach(input => input.disabled = true);
      }
    }
    label input {
      display: none;
    }
    
    label span {
      display: inline-block;
      padding: .2em;
      margin: .2em;
      background-color: lightgray;
    }
    
    input:checked+span {
      background-color: darkgray;
    }
    
    input:checked:disabled+span {
      background-color: green;
    }
    <form name="game"></form>