Cancel/prevent Bootstrap5 collapse when clicking inside links/buttons

Viewed 783

I have a table with parent rows, that have hidden child rows. These child rows are shown/hidden using the bootstrap collapse mechanic. Clicking on the parent row toggles the collapse on the child rows. Parent rows and their childs can be dynamically created.

Parent rows can contain links and buttons. I want to cancel/prevent the collapse toggle when clicking on these. A simple minimum example is given below.

A similar question was asked here: stackoverflow - prevent bootstrap collapse from collapsing

The event.stopPropgation(); does not seem to work (see below), not even when targeting the link/button directly (which anyway probably won't help with dynamically created elements).

I've tried to listen to the collapse events from Bootstrap and use the .toggle() method to undo the collapse, but this (obviously) leads to a recursive event.

Is there a simple method to prevent the links/buttons inside the collapse-triggering row from toggling the collapse?

$("#createNewRow").on("click", function() {
    var number = Math.round(Math.random() * 100000);
  new_row = `
    <tr data-bs-toggle="collapse" data-bs-target=".row`+number+`-child">
        <td>
        This is a dynamically created parent row. Click me to toggle childs.
        <a class="child-link" href="!#">A Link!</a>
        <button class="child-button">A button!</button>
      </td>
    </tr>
  <tr class="collapse child row`+number+`-child"><td>I'm a child row!</td></tr>
  <tr class="collapse child row`+number+`-child"><td>I'm another child row!</td></tr>
  <tr class="collapse child row`+number+`-child"><td>I'm yet another child row!</td></tr>
  `;
  $("#main-table").append(new_row);
});

$(".child-link").on("click", function(e) {
    console.log("The link was clicked!");
  e.stopPropagation();
});

$(".child-button").on("click", function(e) {
    console.log("The button was clicked!");
  e.stopPropagation();
});

$(document).on("click", ".child-link", function(e) {
    console.log("The link was clicked!");
  e.stopPropagation();
});

$(document).on("click", ".child-button", function(e) {
    console.log("The button was clicked!");
  e.stopPropagation();
});
tr {
  background-color: #fcf;
}

.child {
  background-color: #efd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css">

<div class="container">
  <table class="w-100" id="main-table">
  <tr data-bs-toggle="collapse" data-bs-target=".row1-child">
    <td>
      This is a parent row. Click me to toggle childs.
      <a class="child-link" href="!#">A Link!</a>
      <button class="child-button">A button!</button>
    </td>
  </tr>
  <tr class="collapse child row1-child"><td>I'm a child row!</td></tr>
  <tr class="collapse child row1-child"><td>I'm another child row!</td></tr>
  <tr class="collapse child row1-child"><td>I'm yet another child row!</td></tr>
  </table>
</div>

<button class="mt-4" id="createNewRow">Create a new set of rows!</button>

1 Answers

This is very good question! The propagation of events is tricky,

You need to conditionally assign an event handler that prevents the Bootstrap collapse events (show and hide) from occurring. The Collapse event will only occur when the parent tr is clicked.

var eventHandler = function (e) {
     console.log("The collapse event was prevented!", e);
     e.preventDefault();
     e.stopPropagation();
}

// initially prevent collapse from toggling
$(".row1-child").on("show.bs.collapse", eventHandler)

// prevent the click event on child links
$(".child-link").on("click", function(e) {
    console.log("The link was clicked!");
    e.stopPropagation();
    e.preventDefault();
});

// prevent the click event on child buttons
$(".child-button").on("click", function(e) {
    console.log("The button was clicked!");
    e.stopPropagation();
    e.preventDefault();
});

// when the parent row is clicked manually toggle the collapsible elements and re-attach event listeners
$("[data-bs-toggle]").on("click", function(e) {    
    console.log("The parent was clicked! dispatch");
    $(".row1-child").off("show.bs.collapse", eventHandler)
    $(".row1-child").off("hide.bs.collapse", eventHandler)
    $(".row1-child").collapse('toggle')
    $(".row1-child").on("show.bs.collapse", eventHandler)
    $(".row1-child").on("hide.bs.collapse", eventHandler)
});

Demo

Related