Home > Blockchain >  More options button not working in multiple times
More options button not working in multiple times

Time:09-27

I created a More button with dropdown options. The issue is this that the dropdown options are not working when I am using the same code multiple times. It's working only for the first more button. How do I use a single code with same classes with multiple times?

var el = document.querySelector('.more');
var btn = el.querySelector('.more-btn');
var menu = el.querySelector('.more-menu');
var visible = false;

function showMenu(e) {
  e.preventDefault();
  if (!visible) {
    visible = true;
    el.classList.add('show-more-menu');
    menu.setAttribute('aria-hidden', false);
    document.addEventListener('mousedown', hideMenu, false);
  }
}

function hideMenu(e) {
  if (btn.contains(e.target)) {
    return;
  }
  if (visible) {
    visible = false;
    el.classList.remove('show-more-menu');
    menu.setAttribute('aria-hidden', true);
    document.removeEventListener('mousedown', hideMenu);
  }
}
btn.addEventListener('click', showMenu, false);
.more_options{position:relative}.more-btn,.more-menu-btn{background:none;border:0 none;line-height:normal;overflow:visible;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;width:100%;text-align:left;outline:none;cursor:pointer}.more-dot{background-color:#aab8c2;margin:0 auto;display:inline-block;width:7px;height:7px;margin-right:1px;border-radius:50%;transition:background-color 0.3s}.more-menu{position:absolute;top:100%;right:0;z-index:900;padding:10px 0;margin-top:9px;background-color:#fff;border:1px solid #ccd8e0;border-radius:4px;box-shadow:1px 1px 3px rgba(0,0,0,0.25);opacity:0;transform:translate(0, 15px) scale(.95);transition:transform 0.1s ease-out, opacity 0.1s ease-out;pointer-events:none}.more-menu-caret-outer,.more-menu-caret-inner{position:absolute;display:inline-block;margin-left:-1px;font-size:0;line-height:1}.more-menu-caret-outer{border-bottom:10px solid #c1d0da;border-left:10px solid transparent;border-right:10px solid transparent;height:auto;left:0;top:0;width:auto}.more-menu-caret-inner{top:1px;left:1px;border-left:9px solid transparent;border-right:9px solid transparent;border-bottom:9px solid #fff}.more-menu-items{margin:0;list-style:none;padding:0}.more-menu-item{display:block}.more-menu-btn{min-width:100%;color:#66757f;cursor:pointer;display:block;font-size:13px;line-height:18px;padding:5px 20px;position:relative;white-space:nowrap}.more-menu-item:hover{background-color:#63734f }.more-menu-item:hover .more-menu-btn{color:#fff}.more-btn:hover .more-dot, .show-more-menu .more-dot{background-color:#516471}.show-more-menu .more-menu{opacity:1;transform:translate(0, 0) scale(1);pointer-events:auto}
<div class="more more_options">
  <div class="more-btn">
    <span class="more-dot"></span>
    <span class="more-dot"></span>
    <span class="more-dot"></span>
  </div>
  <div class="more-menu">
    <div class="more-menu-caret">
      <div class="more-menu-caret-outer"></div>
      <div class="more-menu-caret-inner"></div>
    </div>
    <ul class="more-menu-items" tabindex="-1" role="menu" aria-labelledby="more-btn" aria-hidden="true">
      <li class="more-menu-item" role="presentation">
        <button type="button" class="more-menu-btn" role="menuitem">Report</button>
      </li>
      <li class="more-menu-item" role="presentation">
        <button type="button" class="more-menu-btn" role="menuitem">Download</button>
      </li>
      <li class="more-menu-item" role="presentation">
        <button type="button" class="more-menu-btn" role="menuitem">See All</button>
      </li>
    </ul>
  </div>
</div>
<!--more options button two begin-->
  

CodePudding user response:

There are quite some things you have to change in your code. At first, there is an important logic step:

If you want to handle more than one menu, you must not set child items on startup. Your code starts with

var btn = el.querySelector('.more-btn');
var menu = el.querySelector('.more-menu');
var visible = false;

But if you want to handle multiple menus, what does e.g. the visible say? Does it say that one menu is visible? Or that all menus are visible? The same thing goes for your show and hide functions, which menu should they show? All? None? A random one?

Since you want to address the menu that is currently clicked on, you have to declare your variables in your click listener. Plus, since you are handling multiple buttons, you have to change your code to use NodeLists. Those are array-like structures that contain all of your buttons.


Check out the code below. This is your code but working for more than one more menu. Here are some explanations:

First you find ALL menus that exist, not just the first. Therefore change querySelector() to querySelectorAll(). To go through all menus and bind the click event to all menus, one can use the forEach() function.

The showMenu() function then checks the this which is the clicked more menu. It decides whether this menu is visible (by presence or absence of the show-more-menu class) and sets the menu to be visible if it is not.

The hideMenu() is bound to the document in general. If one clicks a more menu and then one clicks an other more menu, the first menu should be hidden. Therefore this function hides all menus but the clicked one. This again goes through all more items and checks if the clicked one, the e.target is the current one in the loop. If not, it is hidden. If so, it is kept.

// get ALL more elements
let more = document.querySelectorAll(".more");

if(more !== null){
  // add the showMenu() listener to ALL menus
  more.forEach(function(el){
    el.addEventListener('click', showMenu, false);
  });

  // add the hideMenu() listener in general
  document.addEventListener('mousedown', hideMenu, false);
}

function showMenu(e) {
  // handle the CLICKED menu, note that the corresponding object is only
  // know WHILE or AFTER THE CLICK, not when initializing, therefore 
  // declare variables here, not at the top
  let visible = this.classList.contains("show-more-menu");
  e.preventDefault();
  
  if (!visible) {
    this.classList.add('show-more-menu');
    
    // get the .more-menu that belongs to the .more, not all .more-menus
    let menu = this.querySelector(".more-menu");
    menu.setAttribute('aria-hidden', false);
  }
}

function hideMenu(e) {
  // hide each menu that is currently not clicked on
  more.forEach(function(el){
    if (!el.contains(e.target) && el != e.target) {
      el.classList.remove('show-more-menu');

      let menu = el.querySelector(".more-menu");
      menu.setAttribute('aria-hidden', true);
    }
  });
}
.more_options{position:relative}.more-btn,.more-menu-btn{background:none;border:0 none;line-height:normal;overflow:visible;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;width:100%;text-align:left;outline:none;cursor:pointer}.more-dot{background-color:#aab8c2;margin:0 auto;display:inline-block;width:7px;height:7px;margin-right:1px;border-radius:50%;transition:background-color 0.3s}.more-menu{position:absolute;top:100%;right:0;z-index:900;padding:10px 0;margin-top:9px;background-color:#fff;border:1px solid #ccd8e0;border-radius:4px;box-shadow:1px 1px 3px rgba(0,0,0,0.25);opacity:0;transform:translate(0, 15px) scale(.95);transition:transform 0.1s ease-out, opacity 0.1s ease-out;pointer-events:none}.more-menu-caret-outer,.more-menu-caret-inner{position:absolute;display:inline-block;margin-left:-1px;font-size:0;line-height:1}.more-menu-caret-outer{border-bottom:10px solid #c1d0da;border-left:10px solid transparent;border-right:10px solid transparent;height:auto;left:0;top:0;width:auto}.more-menu-caret-inner{top:1px;left:1px;border-left:9px solid transparent;border-right:9px solid transparent;border-bottom:9px solid #fff}.more-menu-items{margin:0;list-style:none;padding:0}.more-menu-item{display:block}.more-menu-btn{min-width:100%;color:#66757f;cursor:pointer;display:block;font-size:13px;line-height:18px;padding:5px 20px;position:relative;white-space:nowrap}.more-menu-item:hover{background-color:#63734f }.more-menu-item:hover .more-menu-btn{color:#fff}.more-btn:hover .more-dot, .show-more-menu .more-dot{background-color:#516471}.show-more-menu .more-menu{opacity:1;transform:translate(0, 0) scale(1);pointer-events:auto}
<div class="more more_options">
  <div class="more-btn">
    <span class="more-dot"></span>
    <span class="more-dot"></span>
    <span class="more-dot"></span>
  </div>
  <div class="more-menu">
    <div class="more-menu-caret">
      <div class="more-menu-caret-outer"></div>
      <div class="more-menu-caret-inner"></div>
    </div>
    <ul class="more-menu-items" tabindex="-1" role="menu" aria-labelledby="more-btn" aria-hidden="true">
      <li class="more-menu-item" role="presentation">
        <button type="button" class="more-menu-btn" role="menuitem">Report</button>
      </li>
      <li class="more-menu-item" role="presentation">
        <button type="button" class="more-menu-btn" role="menuitem">Download</button>
      </li>
      <li class="more-menu-item" role="presentation">
        <button type="button" class="more-menu-btn" role="menuitem">See All</button>
      </li>
    </ul>
  </div>
</div>
<!--more options button two begin-->
<br />
<br />
<div class="more more_options">
  <div class="more-btn">
    <span class="more-dot"></span>
    <span class="more-dot"></span>
    <span class="more-dot"></span>
  </div>
  <div class="more-menu">
    <div class="more-menu-caret">
      <div class="more-menu-caret-outer"></div>
      <div class="more-menu-caret-inner"></div>
    </div>
    <ul class="more-menu-items" tabindex="-1" role="menu" aria-labelledby="more-btn" aria-hidden="true">
      <li class="more-menu-item" role="presentation">
        <button type="button" class="more-menu-btn" role="menuitem">Report</button>
      </li>
      <li class="more-menu-item" role="presentation">
        <button type="button" class="more-menu-btn" role="menuitem">Download</button>
      </li>
      <li class="more-menu-item" role="presentation">
        <button type="button" class="more-menu-btn" role="menuitem">See All</button>
      </li>
    </ul>
  </div>
</div>

  • Related