Random selections that don’t repeat until resetting

Goal: Highlight different ‘cards’ at random upon clicking the ‘Go’ button. Don’t display the same card twice, unless we’ve already cycled through all of the available cards.

Each card nodeList item is added to an array (cardsCollection), which we then get the length of, so that we know how many cards there are available.

Another array (cardsSelected) is empty by default.

Upon clicking the “Go” button, we generate a random number that is less than the amount of items in the ‘cardsCollection’ array.

Then we check the ‘cardsSelected’ array to see if that number exits within it. If it does, then we generate another random number until successfully generating a number that does not exist within the ‘cardsSelected’ array.

If the length of the ‘cardsCollection’ array becomes larger than the length of the ‘cardsSelection’ array, then we know there are no more possible random numbers to select from and so we reset the ‘cardsSelection’ array and start over.

If the number was successfully added to the ‘cardsSelected’ array then we remove the ‘card–unselected’ class from the respective ‘card’ element by matching its ‘data-card-id’ attribute. This effectively makes the card appear selected, or highlighted, or whatever your desired visual outcome is.

CodePen

JavaScript

// highlight different 'cards' randomly upon clicking the 'go' button
// never displays the same card twice in a row (well, unless when resetting)
// once all cards have been displayed once, then it resets to display them again in another random order

const random = () => {
    const button = document.querySelector('button[data-type="randomize"]')
    const cards = document.querySelectorAll('.card')

    // array that contains our card IDs
    const cardsCollection = Array.from(cards, (card) => card.getAttribute('data-card-id'));

    // array that keeps track of cards that have already been selected
    const cardsSelected = []

    button.addEventListener('click', () => {
        // generate a random number (randomNumber) that is less than the amount of items in the cardsCollection array
        // check if the random number exists as an index in the cardsSelected array
        // if it does, then run again
        // if it does not, then push that random number into the cardsSelected array
        let randomSelection;
        do {
            const randomNumber = Math.floor(Math.random() * cardsCollection.length)
            randomSelection = cardsCollection[randomNumber]
        } while (cardsSelected.includes(randomSelection))
        cardsSelected.push(randomSelection)

        // if the cardsSelected array contains all of the possible random numbers
        // then reset it to 0 so that it can start over
        if (cardsSelected.length >= cardsCollection.length) {
            cardsSelected.length = 0
        }

        cards.forEach((card) => {
            // hide all cards by adding the appropriate CSS class
            card.classList.add('card--unselected')

            // display the randomly selected card by adding the appropriate CSS class
            if (card.dataset.cardId === randomSelection) {
                card.classList.remove('card--unselected')
            }
        })
    })
}
random()

HTML

<button data-type="randomize">Go</button>

<div class="card | card--unselected" data-card-id="1">
    <h3>Selection 1</h3>
</div>

<div class="card | card--unselected" data-card-id="2">
    <h3>Selection 2</h3>
</div>

<div class="card | card--unselected" data-card-id="3000">
    <h3>Selection 3000</h3>
</div>

<div class="card | card--unselected" data-card-id="50">
    <h3>Selection 50</h3>
</div>

CSS

.card {
  background-color: blue;
}

.card--unselected {
  background-color: red;
}