Home > OS >  Toggle a classlist within a foreach loop in Javascript
Toggle a classlist within a foreach loop in Javascript

Time:12-16

I think I'm very close to the solution. I have created a function in Javascript to open and close tabs on click. I have successfully achieved being able to open and close them on click, but I would like to close the tab only when I'm clicking on its header. Any help on how I could do this?

    const openCategories = () => {
        const categories = document.querySelectorAll('.single-category');

        categories.forEach((category) => { 
            category.addEventListener('click', () => {
                category.classList.toggle('opened');
            });
        });
    }

    openCategories();
    <div id="store-categories">

        <div id="category-header">
        <span>Categories</span>
        <div id="toggle-all-categories"><span>X</span></div>
        </div>
                        
        <div id="category-all">
                            
            <div >
                <div >
                    <h3>first category</h3>
                    <div ><span>X</span></div>
                </div>
                <div >
                    [My items]
                </div>
        </div>

            <div >
                <div >
                    <h3>Second category</h3>
                    <div ><span>X</span></div>
                </div>
                <div >
                    [My items]
                </div>
        </div>
    </div>

CodePudding user response:

Pass the Event in your click handler arguments and use Event.target.closest("selector")

The Event.target is the exact element that dispatched the event, which might also be a child of the event delegator. In combination with Element.closest("selector") method you can check if either a closest ancestor / or self — match that specific selector. If such an element is returned, the if will trigger its code in body.

const categories = document.querySelectorAll('.single-category');

categories.forEach((category) => {
  category.addEventListener('click', (evt) => {
    if (evt.target.closest(".single-category-header")) {
      category.classList.toggle('opened');
    }
  });
});
.single-category        .content { display: none; }
.single-category.opened .content { display: block; }
<div id="category-all">
  <div >
    <div ><h3>first category</h3></div>
    <div >111</div>
  </div>
  <div >
    <div ><h3>Second category</h3></div>
    <div >222</div>
  </div>
</div>

Yet better, delegate your events from the parent element:

document.querySelector("#category-all").addEventListener("click", (evt) => {
  const elHeader = evt.target.closest(".single-category-header");
  if (!elHeader) return; // Not a header, do nothing
  elHeader.closest(".single-category").classList.toggle("opened");
});
.single-category        .content { display: none; }
.single-category.opened .content { display: block; }
<div id="category-all">
  <div >
    <div >
      <h3>first category</h3>
    </div>
    <div >111</div>
  </div>
  <div >
    <div >
      <h3>Second category</h3>
    </div>
    <div >222</div>
  </div>
</div>

CodePudding user response:

Please delegate

document.getElementById("store-categories").addEventListener('click', (e) => {
  const tgt = e.target;
  if (!tgt.matches("h3") ) return; // not a header
  const isSingle = tgt.closest("div.single-category");
  if (isSingle) isSingle.querySelector(".content").classList.toggle('opened');
});
document.getElementById("toggle-all-categories").addEventListener("click", e => {
  document.querySelectorAll(".content").forEach(content => content.classList.remove("opened"));
})
.single-category .content { display:none }
.single-category .content.opened { display:block}
<div id="store-categories">

  <div id="category-header">
    <span>Categories</span>
    <div id="toggle-all-categories"><span>X</span></div>
  </div>

  <div id="category-all">

    <div >
      <div >
        <h3>first category</h3>
      </div>
      <div >
        [My items]
      </div>
    </div>

    <div >
      <div >
        <h3>Second category</h3>
      </div>
      <div >
        [My items]
      </div>
    </div>
  </div>

CodePudding user response:

Or add on-other onClick on the header which toggles the openend class on it's parentNode

const openCategories = () => {
    document.querySelectorAll('.single-category').forEach((category) => { 
        category.addEventListener('click', () => {
              category.classList.toggle('opened');
        });
        category.querySelector('.single-category-header').onClick = () =>  {
              event.target.parentNode.classList.toggle('opened');
        };
    });
}
openCategories();
.single-category .content {
  display: none;
}

.single-category.opened .content {
  display: block;
}
<div id="store-categories">

    <div id="category-header">
    <span>Categories</span>
    <div id="toggle-all-categories"><span>X</span></div>
    </div>
                    
    <div id="category-all">
                        
        <div >
            <div >
                <h3>first category</h3>
                <div ><span>X</span></div>
            </div>
            <div >
                [My items]
            </div>
    </div>

        <div >
            <div >
                <h3>Second category</h3>
                <div ><span>X</span></div>
            </div>
            <div >
                [My items]
            </div>
    </div>
</div>

  • Related