RP McMurphy
RP McMurphy

Reputation: 744

Bootstrap dropdown with other button inside clickable parent container

I am trying to add a bootstrap dropdown inside another clickable parent. It's kind of tricky I guess. Here is the Codepen I created with the issue recreated.

Codepen with the issue recreated

Expected behavior: On clicking the dropdown, the dropdown will fire, so will by clicking the other button (optional if too tricky) and none will trigger a click on the parent.

    <div class="card-body parent" style="min-height: 300px;">
      <!-- ORIGINAL DROPPER BUTTON -->
      <div class="dropdown original-dropper mb-5">
        <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Dropdown button
        </button>
        <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
          <a class="dropdown-item" href="#">Action</a>
          <a class="dropdown-item" href="#">Another action</a>
        </div>
      </div>

      <!-- OTHER DROPPER BUTTON -->
      <button class="btn btn-danger other-dropper mt-5 d-inline">Other Button</button>
    </div>

JS:

 $('.parent').on('click', function(e) {
  $(this).css('backgroundColor', 'darkorange');
});

$('.other-dropper').on('click', function(e) {
  e.stopPropagation();
  $('.original-dropper .dropdown-toggle').dropdown();
  console.log('clicked');
});

Upvotes: 3

Views: 2025

Answers (1)

Tom&#225;š Zato
Tom&#225;š Zato

Reputation: 53193

First, it's better to filter an event at the target instead of calling stopPropagation. If you stop propagation of event, it prevents any other global listeners from capturing them. For example, other dropdowns will not close if click event propagation was stopped.

So instead I introduced this neat filter method, that allows you to check if the source of event or it's parents has a class:

/**
 * Checks if any of parent nodes of elm has a class name
 * @param {HTMLElement} elm the starting node
 * @param {string} className
 * @param {HTMLElement} stopAtElm if this parent is reached, search stops
**/
function treeHasClass(elm, className, stopAtElm) {
  while(elm != null && elm != stopAtElm) {
    if(elm.classList.contains(className)) {
      return true;
    }
    elm = elm.parentNode;
  }
  return false;
}

Unfortunately you still need stopPropagation for the button, else the click event would close the drop-down immediately.

Second, you want to call .dropdown("toggle"):

$('.original-dropper .dropdown-toggle').dropdown("toggle");

All together, the javascript is:

$('.parent').on('click', function(e) {
  // Do not trigger if target elm or it's child has `no-orange` class
  if(!treeHasClass(e.target, "no-orange", this)) {
    $(this).css('backgroundColor', 'darkorange');
  }
});

$('.other-dropper').on('click', function(e) {
  e.stopPropagation();
  e.preventDefault();
  $('.original-dropper .dropdown-toggle').dropdown("toggle");
  console.log('clicked');
});

And you want to put no-orange to elements that shouldn't change background color:

<button class="no-orange btn btn-danger other-dropper mt-5 d-inline">Other Button</button>

All together here: https://codepen.io/MXXIV/pen/rNBpepd

Upvotes: 1

Related Questions