1

I'm a beginner in programming. My JavaScript code selects more than one div at a time, i would like it to be possible to select only one at a time.

var ul = document.getElementById("list");

var listItems = ul.getElementsByTagName("div");

for (div of listItems) {
  div.addEventListener('click', function() {
    if (this.classList.contains('active')) {
      this.classList.remove("active");
    } else {
      this.classList.add("active");
    }
  })
}
.active {
  background-color: #D78A45;
}
<div class="grid-container" id="list">
  <div id="1" class="grid-item">1</div>
  <div id="2" class="grid-item">2</div>
  <div id="3" class="grid-item">3</div>
  <div id="4" class="grid-item">4</div>
  <div id="5" class="grid-item">5</div>
  <div id="6" class="grid-item">6</div>
  <div id="7" class="grid-item">7</div>
  <div id="8" class="grid-item">8</div>
  <div id="9" class="grid-item">9</div>
  <div id="10" class="grid-item">10</div>
</div>

when a user clicks on another div, deselects another if there is

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
  • Instead of the if test, 1. remove the class from all elements 2. add it to the clicked one –  May 17 '21 at 19:58
  • A different approach: in the click 1. set an external variable to the `id` of the clicked element 2. again loop over all elements, removing or adding the class based on whether the current id matches the saved id –  May 17 '21 at 20:01
  • Also, look into [the `toggle` method of `classList`](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle). – Heretic Monkey May 17 '21 at 20:01

5 Answers5

1

My advice would be to:

  • save your <li> items to an array with Array.from
  • every time you register a click, clear the "active" class from all elements except the one you want
  • add the "active" class to the element you just clicked

const ul = document.getElementById("list");

// Array.from allows us to use array methods on DOM lists
const listItems = Array.from(ul.getElementsByTagName("div"));

const setThisOneToActive = (event) => {

    // identify the element we just clicked
    const thisLi = event.target;

    // omit that particular element from the list
    const allOtherLis = listItems.filter(li => {
        return (li !== thisLi);
    });

    // loop through resultant list and remove "active" class
    allOtherLis.forEach(li => {
        li.classList.remove('active');
    });

    // add our "active" class
    thisLi.classList.add('active');
};

listItems.forEach(li => {
    li.addEventListener('click', setThisOneToActive);
});
.grid-container {
    display: inline-block;
    position: relative;
    width: 300px;
}

.grid-item {
    width: 50px;
    height: 50px;
    margin: 5px;
    position: relative;
    float: left;
    border: 3px dotted black;
    border-radius: 10px;
    background-color: silver;
    text-align: center;
    font-size: larger;
    cursor: pointer;
}

.grid-item:hover {
    background-color: white;
}

.grid-item.active {
    background-color: #D78A45;
}
<div class="grid-container" id="list">
    <div id="1" class="grid-item">1</div>
    <div id="2" class="grid-item">2</div>
    <div id="3" class="grid-item">3</div>
    <div id="4" class="grid-item">4</div>
    <div id="5" class="grid-item">5</div>
    <div id="6" class="grid-item">6</div>
    <div id="7" class="grid-item">7</div>
    <div id="8" class="grid-item">8</div>
    <div id="9" class="grid-item">9</div>
    <div id="10" class="grid-item">10</div>
</div>
code_monk
  • 9,451
  • 2
  • 42
  • 41
0

You can try storing the element that is currently active in a variable, and then whenever you click on another element, you remove the active class from the previously active div, add it to the div you clicked on and then replace the value of the variable with the current div you clicked on.

So it should look something like:

let ul = document.getElementById("list");

let listItems = ul.getElementsByTagName("div");

let currentlyActiveDiv = null;

for (div of listItems) {
  div.addEventListener('click', function() {
    if (this.classList.contains('active')) {
      this.classList.remove("active");
    } else {
      this.classList.add("active");
      if(currentlyActiveDiv !== null) currentlyActiveDiv.classList.remove('active');
      currentlyActiveDiv = this;
    }
  })
}
Ron B.
  • 1,502
  • 2
  • 7
0

You just missing to reset the elements to their original state before each change request (original state meaning, no element has active class)

simply add

document.querySelectorAll("#list > div").forEach(x => x.className -= "active")

from your original code, with an extra line (and removed redundant code, as you already removing the class name):

var ul = document.getElementById("list");

var listItems = ul.getElementsByTagName("div");

for (div of listItems) {
  div.addEventListener('click', function() {
    // reset every element, no matter what
    document.querySelectorAll("#list > div").forEach(x => x.className -= "active");
    // add the class to the clicked element
    this.classList.add("active");
  })
}
.active {
  background-color: #D78A45;
}
<div class="grid-container" id="list">
  <div id="1" class="grid-item">1</div>
  <div id="2" class="grid-item">2</div>
  <div id="3" class="grid-item">3</div>
  <div id="4" class="grid-item">4</div>
  <div id="5" class="grid-item">5</div>
  <div id="6" class="grid-item">6</div>
  <div id="7" class="grid-item">7</div>
  <div id="8" class="grid-item">8</div>
  <div id="9" class="grid-item">9</div>
  <div id="10" class="grid-item">10</div>
</div>
balexandre
  • 73,608
  • 45
  • 233
  • 342
0

You dont have to get elements by their tag because you already have a class.

Related to that. Be careful with .getElementsByTagName() as it returns a "live node list", which means that it has to re-scan the document to ensure that it keeps its list up to date. This can be extremely wasteful in terms of performance. Instead, .getElementById(), .querySelector() and .querySelectorAll() should probably be the most common ways to scan for individual elements or groups of elements.

Based on Best Practices for Newbies

You can use this:

var ul = document.getElementById("list");

ul.addEventListener("click", function(event) {
  [...ul.getElementsByClassName("grid-item")].forEach(function(item) {
    item.classList.remove("active");
  })

  if (event.target.classList.contains("grid-item")) {
    if (!event.target.classList.contains('active')) {
      event.target.classList.add("active");
      return;
    }
  }

})
.active {
  background-color: #D78A45;
}
<div class="grid-container" id="list">
  <div id="1" class="grid-item">1</div>
  <div id="2" class="grid-item">2</div>
  <div id="3" class="grid-item">3</div>
  <div id="4" class="grid-item">4</div>
  <div id="5" class="grid-item">5</div>
  <div id="6" class="grid-item">6</div>
  <div id="7" class="grid-item">7</div>
  <div id="8" class="grid-item">8</div>
  <div id="9" class="grid-item">9</div>
  <div id="10" class="grid-item">10</div>
</div>
Prosy Arceno
  • 2,616
  • 1
  • 8
  • 32
0

they are a lot of ways, this one is the shorter ?.

look at event delegation, classList methods

const divList  = document.querySelector('#list')
  ,  listItems = document.querySelectorAll('#list > div')
  ;
divList.onclick = ({target}) =>
  {
  if (!target.matches('#list > div')) return // to ignore any clic elsewhere
  
  if ( target.classList.toggle('active'))
    listItems.forEach(item=>
      {
      if (item != target)
        item.classList.remove('active')
      })
  }
#list {
  cursor : pointer;
  width  : 8em;
  } 
#list > div {
  margin           : .2em;
  background-color : #d3d3d366;
  text-align       : center;
  } 
#list > div:hover {
  background-color : grey;
  }
#list > div.active {
  background-color: #D78A45;
  }
<div class="grid-container" id="list">
  <div id="1" class="grid-item">1</div>
  <div id="2" class="grid-item">2</div>
  <div id="3" class="grid-item">3</div>
  <div id="4" class="grid-item">4</div>
  <div id="5" class="grid-item">5</div>
  <div id="6" class="grid-item">6</div>
  <div id="7" class="grid-item">7</div>
  <div id="8" class="grid-item">8</div>
  <div id="9" class="grid-item">9</div>
  <div id="10" class="grid-item">10</div>
</div>
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40