Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mhmmdva's avatar

how to create javascript code with foreach or looping to make it more efficient and easy to read

I have 7 accordions that have arrow icons. And I'm making the code in JavaScript as below, but do I have to repeat it 7x?

const collapsed = document.getElementById('collapsed-parent');
const icon = document.querySelector('#collapsed-parent .fa-angle-down');
collapsed.addEventListener('click', function () {
   if (icon == document.querySelector('#collapsed-parent .fa-angle-down')) {
      icon.classList.remove('fa-angle-down')
      icon.classList.add('fa-angle-up')
      console.log('parent up');
   } else if (icon == document.querySelector('#collapsed-parent .fa-angle-up')) {
      icon.classList.remove('fa-angle-up')
      icon.classList.add('fa-angle-down')
      console.log('parent down');
   }

})

const collapsedChildOne = document.getElementById('collapsed-child-one');
const iconChildOne = document.querySelector('#collapsed-child-one .fa-angle-down');
collapsedChildOne.addEventListener("click", () => {
   arrowUpAndDown(iconChildOne);
   console.log('okk child');
});
const collapsedChildTwo = document.getElementById('collapsed-child-two');
const iconChildTwo = document.querySelector('#collapsed-child-two .fa-angle-down');
collapsedChildTwo.addEventListener("click", () => {
   arrowUpAndDown(iconChildTwo);
   console.log('two');
});
const collapsedChildThree = document.getElementById('collapsed-child-three');
const iconChildThree = document.querySelector('#collapsed-child-three .fa-angle-down');
collapsedChildThree.addEventListener("click", () => {
   arrowUpAndDown(iconChildThree);
   console.log('three');
});
const collapsedChildFour = document.getElementById('collapsed-child-four');
const iconChildFour = document.querySelector('#collapsed-child-four .fa-angle-down');
collapsedChildFour.addEventListener("click", () => {
   arrowUpAndDown(iconChildFour);
   console.log('four');
});

function arrowUpAndDown(icon) {
   const angleDown = "fa-angle-down"
   const angleUp = "fa-angle-up"
   icon.classList.toggle(angleDown)
   icon.classList.toggle(angleUp)
}
0 likes
13 replies
rodrigo.pedra's avatar

@mhmmdva please share your HTML structure

HTML events bubble up the DOM tree until they find a listener, so you can have a single listener to handle all the collapsed children, if they share a common parent element.

rodrigo.pedra's avatar

@jlrdw what do you mean?

I thought Laracasts fetched it from Gravatar

It is loading fine to me... Maybe you could try on a private window, or clearing your cache?

rodrigo.pedra's avatar
Level 56

@mhmmdva I put together a sample proof-of-concept to illustrate how you could handle it

<style>
    .hidden {
        display: none;
    }

    .arrow-down::before {
        content: '93';
    }

    .arrow-up::before {
        content: '91';
    }

    .accordion {
        border: solid 1px black;
    }

    .accordion section h2 {
        margin: 0;
        background-color: grey;
        cursor: default;
        border-bottom: solid 1px black;
    }
</style>

<div class="accordion">
    <section>
        <h2>Section 1 <i class="arrow-down"></i></h2>

        <div class="hidden">
            Lorem ipsum dolor sit amet.
        </div>
    </section>

    <section>
        <h2>Section 2 <i class="arrow-down"></i></h2>

        <div class="hidden">
            Lorem ipsum dolor sit amet.
        </div>
    </section>

    <section>
        <h2>Section 3 <i class="arrow-down"></i></h2>

        <div class="hidden">
            Lorem ipsum dolor sit amet.
        </div>
    </section>
</div>

<script>
    // using a class, instead of an id, allows you to have 
    // multiple accordions on the same page
    document.querySelectorAll('.accordion').forEach(function (accordion) {
        let current;

        // this will toggle CSS classes on a section relevant elements
        function toggleSection(section, isVisible) {
            const icon = section.querySelector('i');
            icon.classList.toggle('arrow-up', isVisible);
            icon.classList.toggle('arrow-down', !isVisible);

            section.querySelector('div').classList.toggle('hidden', !isVisible);
        }

        // by adding the event listener to the parent element
        // a listener is no longer needed for each individual section
        accordion.addEventListener('click', function (event) {
            // stop this event from bubbling up into the DOM tree
            event.stopPropagation();

            // find the event parent section
            const clicked = event.target.closest('section');

            // this will hide the clicked section if it is currently visible
            current = clicked === current ? null : clicked;

            // walk thorugh each section to check its state
            accordion.querySelectorAll('section')
                .forEach((section) => toggleSection(section, section === current));
        });
    });
</script>

Code is functional, just copy and paste, save as an HTML file, and open in a browser to explore how it works.

Modern JS frameworks improved the frontend landscape by a lot, but I feel concepts like event bubbling are being lost.

Hope this helps =)

1 like
rodrigo.pedra's avatar

@jlrdw haha, glad to hear that...

I am sorry if keeping the same avatar disappointed you 😅

mhmmdva's avatar

@rodrigo.pedra

<div class="research research-flush" id="faqlist">
    <div class="research-item" data-aos="fade-up" data-aos-delay="200">
        <h3 class="research-header">
            <button id="collapsed-parent" class="collapsed parent" type="button" 
            data-bs-toggle="collapse" data-bs-target="#research-content">                
                lorem ipsum
                <i class="fas fa-angle-down" style="color: #990000;"></i>
            </button>
        </h3>

        <!-- start  -->
        <div id="research-content" class="research-collapse collapse" data-bs-parent="#faqlist">
            <h3 class="research-header">
                <button id="collapsed-child-one" class="collapsed child" type="button" 
                data-bs-toggle="collapse" data-bs-target="#content-2">
                    basic theme
                    <i class="fas fa-angle-down" style="color: #990000;"></i>
                </button>
            </h3>
        </div>
        <div id="content-2" class="research-collapse collapse" data-bs-parent="#faqlist">
            <div class="research-body">
                // data tables
            </div>
        </div>
        <!-- end  -->

        <!-- start  -->
        <div id="research-content" class="research-collapse collapse" data-bs-parent="#faqlist">
            <h3 class="research-header">
                <button id="collapsed-child-two" class="collapsed child" type="button" 
                data-bs-toggle="collapse" data-bs-target="#content-2">
                    development theme
                    <i class="fas fa-angle-down" style="color: #990000;"></i>
                </button>
            </h3>
        </div>
        <div id="content-2" class="research-collapse collapse" data-bs-parent="#faqlist">
            <div class="research-body">
                // data tables
            </div>
        </div>
        <!-- end  -->
    </div>
</div>
Tray2's avatar

@mhmmdva You can't have elements with the same id on the same page, ids are unique.

mhmmdva's avatar

@Tray2 If it has to be like that, does that mean I have to make as many as possible with the same code?

I've made it like this, but the code doesn't work well :

const p = document.querySelectorAll('.parent'); // button
p.forEach(function(icon){
   
   icon.addEventListener('click', function(){     
      if (icon.classList.contains('.fa-angle-down')) {
         icon.classList.remove('.fa-angle-down');
         icon.classList.add('.fa-angle-up');
         console.log('up');   
      } else {
         icon.classList.remove('.fa-angle-up');
         icon.classList.add('.fa-angle-down');
         console.log('down');
      }
   });
});
Tray2's avatar

@mhmmdva If they are generated with a loop then just give them a numeric suffix like accordian-1, you can build the id if the element with JS

`id="accordian${counter}"`
mhmmdva's avatar

@Tray2 although I don't really understand, I will try it thanks for the advice given

Please or to participate in this conversation.