Vanilla js when key press up and down, change active class

Viewed 594

when i press the up and down keys, i want the selected "div" element to change. How can i achieve this. pressing the down key on the keyboard, the selection needs to go to a bottom line.

document.addEventListener("DOMContentLoaded", function(event) { // <-- add this wrapper
var element = document.querySelectorAll('.element');

    if (element) {
    
      element.forEach(function(el, key){
        
         el.addEventListener('click', function () {
            console.log(key);
         
            el.classList.toggle("active");
            
             element.forEach(function(ell, els){
                 if(key !== els) {
                     ell.classList.remove('active');
                 }
                  console.log(els);
             });
         });
      });
    }
});
.element{
  background: gray;
  width:200px;
  padding:10px;
  margin-bottom:1px;
}

.element.active{
  background: red;
}
<section>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
</section>

3 Answers

Pass in the array length to the listener function. If you have that function return a new function (a closure) you can keep a note of the the current element index, and compare it to 0 or the length of the array. And because you keep a record of the current index you don't need to loop over all the elements to remove the active class, just remove the class from the element you're on, and add it to the next one.

// Grab all the elements
var elements = document.querySelectorAll('.element');

// Call a function as a key press handler that returns 
// a new function (the actual listener)
// that deals with the key presses. We do this
// because we want to keep a variable `index` to identify which 
// element in the collection we're on, and a closure
// (a function that hangs on to its local lexical environment
// when it's returned) is very convenient
document.addEventListener('keydown', handler(elements.length - 1), false);

// Set the initial element
elements[0].classList.add('active');

// So this is the handler that returns the closure.
// We pass in the array length, set up the index variable,
// and return a new listener function that deals with the key presses
function handler(arrayLength) {

  let index = 0;

  return function(e) {

    if (e.code === 'ArrowUp' && index > 0) {
      elements[index].classList.remove('active');
      --index;
      elements[index].classList.add('active');
    }

    if (e.code === 'ArrowDown' && index < arrayLength) {
      elements[index].classList.remove('active');
      ++index;
      elements[index].classList.add('active');
    }

  }

};
.element { background: gray; width: 200px; padding: 10px; margin-bottom: 1px; }
.active { background: red; }
<section>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
</section>

Additional documentation

Do you mean something like this?

document.addEventListener("DOMContentLoaded", function(event) { // <-- add this wrapper
var element = document.querySelectorAll('.element');

    if (element) {
    
      element.forEach(function(el, key){
        
         el.addEventListener('click', function () {
         
            el.classList.toggle("active");
            
             element.forEach(function(ell, els){
                 if(key !== els) {
                     ell.classList.remove('active');
                 }
             });
         });
      });
      document.body.onkeydown = function(event) {
        if(event.key=="ArrowUp" && document.getElementsByClassName("active").length > 0) {
          event.preventDefault();
          var active = document.getElementsByClassName("active")[0];
          if(active.previousElementSibling)
            active.previousElementSibling.classList.add("active");
          active.classList.remove("active");
          return false;
        };
        if(event.key=="ArrowDown" && document.getElementsByClassName("active").length > 0) {
          event.preventDefault();
          var active = document.getElementsByClassName("active")[0];
          if(active.nextElementSibling)
            active.nextElementSibling.classList.add("active");
          active.classList.remove("active");
          return false;
        }
        else if(event.key=="ArrowDown") {
          event.preventDefault();
          element[0].classList.add("active"); //If nothing is selected, use element[element.legth − 1] for the last element]
          return false;
        };
      };
    };
});
.element{
  background: gray;
  width:200px;
  padding:10px;
  margin-bottom:1px;
}

.element.active{
  background: red;
}
<section>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
</section>

For selecting the element, I would suggest using a single function (setSelected in the example), then you can more easily set the active element, whether it be from a click, a keydown, or another call. Besides that, you don't spread the logic over several places, in case you want something extra to happen when an element becomes active.

document.addEventListener("DOMContentLoaded", function(event) { // <-- add this wrapper
    const elements = document.querySelectorAll('.element');

    if (elements) {    
      let selected = -1;
      
      function setSelected(index){ //single entry point for selecting an element
        if(selected===index)return;
        const activeClass = 'active';
        if(selected >=0)elements[selected].classList.remove(activeClass);
        elements[selected = index].classList.add(activeClass);
      }
      
      elements.forEach((el, index) => el.onclick = e=>setSelected(index));
      document.body.onkeydown  = e=>{ 
        if(selected < 0)return; //no element selected yet
        let i = selected;
        const key = e.key;
        if(key==='ArrowDown')
          i++;
        else if(key==='ArrowUp')
          i--;
        else return false;
        
        setSelected(i = Math.max(0,Math.min(elements.length-1,i)));
        e.preventDefault();
        return true;
      };
    }
});
.element{
  background: gray;
  width:200px;
  padding:10px;
  margin-bottom:1px;
}

.element.active{
  background: red;
}
<section>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
  <div class="element">BOX</div>
</section>

Related