Home > Back-end >  Show menu onclick on multiple posts with same classes
Show menu onclick on multiple posts with same classes

Time:06-25

So I made a posting system with PHP and it's finally working, though, I have a problem. I coded a javascript that opens the post menu, but it won't open at all. If I were to use the getElementById function, only the first post will be selected so the other posts' menu won't open, so I tried using the getElementsByClassName, but now the menu won't open in any posts.

Here is my code:

javascript

let postMenu = document.getElementsByClassName("post-Menu");
let morebtn = document.getElementsByClassName("morebtn");

morebtn.onclick = function() {
    postMenu.classList.toggle("postmenShow");
}

html

<div >
    <div >
        <svg viewBox="0 0 24 24" aria-hidden="true" >
            <g>
                <circle cx="5" cy="12" r="2"></circle>
                <circle cx="12" cy="12" r="2"></circle>
                <circle cx="19" cy="12" r="2"></circle>
            </g>
        </svg>
    </div>
    <div >
        <a href="">
            <div ><i ></i>&nbsp;&nbsp;Delete</div>
        </a>
        <a href="">
            <div ><i ></i>&nbsp;&nbsp;Edit</div>
        </a>
    </div>
</div>

CodePudding user response:

It is hard to guess the exact structure of your HTML, but it could be something like this. I added a #wrapper element and have the event listener on that. To get the right .norebtn I use Element.closest() – so the .morebtn that is closest to the child element that was clicked get selected. In this example I just change the class name on the .morebtn adding .on and then use the CSS selector to style the following .post-Menu from the .morebtn.

let wrapper = document.getElementById('wrapper');

wrapper.addEventListener('click', e => {
  let morebtn = e.target.closest('.morebtn');
  if(morebtn){
    morebtn.classList.toggle("on");
  }
});
.post-Menu {
  display: none;
}

.morebtn {
  width: 50px;
}

.morebtn.on {
  fill: red;
}

.morebtn.on .post-Menu {
  display: block;
}
<div id="wrapper">
  <div >
    <div >
      <svg viewBox="0 0 24 24" aria-hidden="true" >
        <g>
          <circle cx="5" cy="12" r="2"></circle>
          <circle cx="12" cy="12" r="2"></circle>
          <circle cx="19" cy="12" r="2"></circle>
        </g>
      </svg>
    </div>
    <div >
      <a href="">
        <div ><i ></i>&nbsp;&nbsp;Delete</div>
      </a>
      <a href="">
        <div ><i ></i>&nbsp;&nbsp;Edit</div>
      </a>
    </div>
  </div>
</div>

CodePudding user response:

As suggested previously it is likely that you had duplicate ID attributes within your HTML but as you were not using document.getElementById to address these elements Javascript would not have had cause for concern but it is still technically invalid markup. Using document.getElementsByClassName returns a live nodelist see here for details so each element in this collection needs to be treated individually if you are to assign an event listener to each one. ( An alternative approach is to assign a delegated event listener to a higher level parent element and inspect all click events )

The code below uses document.querySelectorAll which behaves in a similar manner to getElementsByClassName and returns a nodelist through which you can iterate and assign the event handler. The clickhandler inspects the event.target to find which element invoked the click - from there it s possible to find the sibling element with class .post-Menu and modify the visibility therein.

const clickhandler = (e) => {
  let el = e.target == e.currentTarget ? e.target : e.target.closest('div');
  el.parentNode.querySelector('.post-Menu').classList.toggle("postmenShow");
};

document.querySelectorAll('div.morebtn').forEach(bttn => bttn.addEventListener('click', clickhandler));
svg {
  width: 2rem;
  height: 2rem;
  fill: red;
  margin: 0.5rem 0;
}

.post-Menu {
  display: none;
}

.postmenShow {
  display: block
}
<div >
  <div >
    <svg viewBox="0 0 24 24" aria-hidden="true" >
            <g>
                <circle cx="5" cy="12" r="2"></circle>
                <circle cx="12" cy="12" r="2"></circle>
                <circle cx="19" cy="12" r="2"></circle>
            </g>
        </svg>
  </div>
  <div >
    <a href="#">
      <div ><i ></i>&nbsp;&nbsp;Delete</div>
    </a>
    <a href="#">
      <div ><i ></i>&nbsp;&nbsp;Edit</div>
    </a>
  </div>
</div>


<div >
  <div >
    <svg viewBox="0 0 24 24" aria-hidden="true" >
            <g>
                <circle cx="5" cy="12" r="2"></circle>
                <circle cx="12" cy="12" r="2"></circle>
                <circle cx="19" cy="12" r="2"></circle>
            </g>
        </svg>
  </div>
  <div >
    <a href="#">
      <div ><i ></i>&nbsp;&nbsp;Delete</div>
    </a>
    <a href="#">
      <div ><i ></i>&nbsp;&nbsp;Edit</div>
    </a>
  </div>
</div>

An alternative approach using a delegated event listener rather than assigning the clickhandler to each element.

// utility to try to find parent element in the dom
const getparent=(e,expr)=>{
    let n=e.target;
    do{
        if( Element.prototype.matches.call( n, expr ) ) return n;       
        n=n.previousElementSibling;
    }while( n!==null && n.nodeType===1 );
    
    
    n=e.target;
    do{
        if ( Element.prototype.matches.call( n, expr ) ) return n;
        n=n.parentNode;
    }while( n!==null && n.nodeType===1 );
    return n;
};

const delegatedclickhandler=(e) => {
  if (e.target != e.currrentTarget) {
    let el=getparent(e,'div.postmenu');
    el.querySelector('.post-Menu').classList.toggle("postmenShow");
  }
};

document.body.addEventListener('click', delegatedclickhandler);
svg {
  width: 2rem;
  height: 2rem;
  fill: red;
  margin: 0.5rem 0;
}

.post-Menu {
  display: none;
}

.postmenShow {
  display: block
}
<div >
  <div >
    <svg viewBox="0 0 24 24" aria-hidden="true" >
                <g>
                    <circle cx="5" cy="12" r="2"></circle>
                    <circle cx="12" cy="12" r="2"></circle>
                    <circle cx="19" cy="12" r="2"></circle>
                </g>
            </svg>
  </div>
  <div >
    <a href="#">
      <div ><i ></i>&nbsp;&nbsp;Delete</div>
    </a>
    <a href="#">
      <div ><i ></i>&nbsp;&nbsp;Edit</div>
    </a>
  </div>
</div>

<div >
  <div >
    <svg viewBox="0 0 24 24" aria-hidden="true" >
                <g>
                    <circle cx="5" cy="12" r="2"></circle>
                    <circle cx="12" cy="12" r="2"></circle>
                    <circle cx="19" cy="12" r="2"></circle>
                </g>
            </svg>
  </div>
  <div >
    <a href="#">
      <div ><i ></i>&nbsp;&nbsp;Delete</div>
    </a>
    <a href="#">
      <div ><i ></i>&nbsp;&nbsp;Edit</div>
    </a>
  </div>
</div>

  • Related