skip to Main Content

I have a div with 5 elements, and I only want to show 1 at a time.
Is there a way to change .classname:nth-child(1) to .classname:nth-child(3) in javascript?
Or do I need a different approach?
I have a display none on the parent and i want to display flex the nth child

2

Answers


  1. I suppose this different approach may be viable. The snippet uses event delegation:

    document.addEventListener(`click`, handle);
    
    function handle(evt) {
      if (evt.target.id === `next`) {
        let next;
        // remove .active from all elements
        document.querySelectorAll(`.className`).forEach(
          (elem, i) => {
            if (elem.classList.contains(`active`)) {
              // Note: nth-child is not zero based
              next = i + 2 > 5 ? 1 : i + 2; 
            }
            elem.classList.remove(`active`);
          });
        // add .active to the next element 
        document.querySelector(`.className:nth-child(${next})`)
          .classList.add(`active`);
      }
    }
    .className {
      display: none;
    }
    
    .active {
      display: block;
    }
    <div class="className active">div 1</div>
    <div class="className">div 2</div>
    <div class="className">div 3</div>
    <div class="className">div 4</div>
    <div class="className">div 5</div>
    
    <button id="next">next</button>

    In reply to @tacoshys

    It is inefficient and consumes way more resources and time than
    necessary

    I beg to differ. Let’s cite Donald Knuth here

    Programmers waste enormous amounts of time thinking about, or worrying
    about, the speed of noncritical parts of their programs, and these
    attempts at efficiency actually have a strong negative impact when
    debugging and maintenance are considered. We should forget about small
    efficiencies, say about 97% of the time: premature optimization is the
    root of all evil. Yet we should not pass up our opportunities in that
    critical 3%."

    About inefficiency of event delegation I cite the linked page @javascript.info:

    … the delegation may add CPU load, because the container-level
    handler reacts on events in any place of the container, no matter
    whether they interest us or not. But usually the load is negligible,
    so we don’t take it into account.

    It does not scale

    Sure, it’s up to anyone to make it more scalable. The snippet was just an rough indication for how to solve OPs problem.

    (it is hard to read)

    Readability, like beauty, is in the eye of the beholder.

    Now about your code: the const you define for the div.className nodeList may give you trouble later on, e.g. when elements are added dynamically. So, about scalability …

    The latter is demonstrated in the next snippet (which uses your idea – which, by the way, is not a bad idea ;).

    document.addEventListener(`click`, handle);
    const logLine = txt => (console.clear(), txt && console.log(txt));
    const ALL_ELEMS = document.querySelectorAll(`.className`);
    
    for (let i = 0; i < 5; i += 1) {
      addElemDemo(i);
    }
    
    logLine(`There are now ${document.querySelectorAll(`.className`)
      .length} div.className in the document. `);
    
    function activate(activeClass) {
      logLine();
      let activeIndex = [...ALL_ELEMS]
        .findIndex(el => el.classList.contains(activeClass));
      if (activeIndex >= 0) {
        ALL_ELEMS[activeIndex].classList.remove(activeClass);
        return ALL_ELEMS[activeIndex + 1 >= ALL_ELEMS.length ? 
          0 : activeIndex + 1].classList.add(activeClass);
      }
      return logLine(`Well, there you have it ... ` + 
        `you have to make a round trip using 'no really, next'`);
    }
    
    function activate4Real(selector, activeClass) {
      const all_elems = document.querySelectorAll(selector);
      let activeIndex = [...all_elems]
        .findIndex(el => el.classList.contains(activeClass));
      if (activeIndex + 1 > ALL_ELEMS.length - 1) {
        logLine(`Try clicking the 'next' button now`);
      }  
      all_elems[activeIndex].classList.remove(activeClass);
      all_elems[activeIndex === all_elems.length-1 ? 
        0 : activeIndex + 1].classList.add(activeClass);
    }
    
    function addElemDemo(index) {
      const lastIndex = ALL_ELEMS.length + index;
      document.querySelector(`#next`).insertAdjacentElement(
       `beforebegin`, 
        Object.assign(document.createElement(`div`),
        {className: `className`, textContent: `div ${lastIndex + 1}`} ) );
    };
    
    function handle(evt) {
      if (evt.target.id === `next`) {
        return activate( evt.target.dataset.activeclass );
      }
      
      if (evt.target.id === `reallyNext`) {
        return activate4Real(
          evt.target.dataset.forselector,
          evt.target.dataset.activeclass );
      }
    }
    .className {
      display: none;
    }
    
    .active {
      display: block;
    }
    <div class="className active">div 1</div>
    <div class="className">div 2</div>
    <div class="className">div 3</div>
    <div class="className">div 4</div>
    <div class="className">div 5</div>
    
    <button 
      id="next" 
      data-activeclass="active";
     >next</button>
     
    <button 
      id="reallyNext" 
      data-forselector=".className"
      data-activeclass="active";
     >no really, next</button>
    Login or Signup to reply.
  2. @Kooilnc Answer works but has 2 major issues:

    1. It is inefficient and consumes way more resources and time than necessary
    2. It does not scale
    3. (it is hard to read)

    Inefficient

    For once the code appends the EventListener to the whole document and then checks on every click if the button was clicked. The `EventListener should be added to the button itself:

    const BUTTON = documents.querySelector('#next');
    BUTTON.addEventListener('click', function() {
      ...
    })
    

    Then he uses querySelectorAll which returns a NodeList. Then he iterates through the NodeList and checks every Element if it has the "active" class. If he has found that element in his iteration he removes that class. The efficient (fast and resource-saving) solution is to select the element that has the class directly and remove the class from that element:

    document.querySelector('.className.active').classList.remove('active);
    

    No need for an iteration that takes more time and resources.

    Scalability

    Scalability should always be a concern. The code from the mentioned answer has the issue that it requires to be exact 5 elements. It will no longer work correctly if you have more or fewer elements. As such it requires manual maintenance and fixing as soon as you add or removes content.
    A good and clean code should not care about how many elements you have to work correctly. The trick here is to check in the script how many elements you have by using NodeList.length.

    Improved Solution

    const BUTTON = document.querySelector('#next');
    const ELEMENTS = document.querySelectorAll('.className')
    
    BUTTON.addEventListener('click', function() {
      let active = document.querySelector('.className.active');
      let indexOfActive = [].indexOf.call(ELEMENTS, active) + 1;
      
      active.classList.remove('active');
      
      if (indexOfActive === ELEMENTS.length) {
        indexOfActive = 0;
      }
        
      ELEMENTS[indexOfActive].classList.add('active');
    })
    .className {
      display: none;
    }
    
    .active {
      display: block;
    }
    <div class="className active">div 1</div>
    <div class="className">div 2</div>
    <div class="className">div 3</div>
    <div class="className">div 4</div>
    <div class="className">div 5</div>
    
    <button id="next">next</button>

    How does the code Work?

    1. It creates a NodeList out of all the elements
    2. Then it takes the index from the element with the active class in
      the NodeList
    3. It adds + 1 to the index to select the next element in the NodeList unless the index is equal to the length of the NodeList in which case it resets the index to 0
    4. It adds the active class to the next element
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search