twbs / bootstrap

The most popular HTML, CSS, and JavaScript framework for developing responsive, mobile first projects on the web.
https://getbootstrap.com
MIT License
169.99k stars 78.74k forks source link

Tabs click events are handled at capture phase #36063

Open romaricpascal opened 2 years ago

romaricpascal commented 2 years ago

Prerequisites

Describe the issue

As a blanket way to intercept and prevent click events from triggering certain actions after editing some form fields, I'm trying to use events capture phase to intercept them as early as possible and "cancel" them.

However, in the case of Bootstrap tabs, Bootstrap's EventHandler seems to attach its listener on document, for the capture phase, preventing to do anything earlier:

The addEventListener call receives a 3rd delegation parameter, turned to true by normalizeParameters because the Tab's setup code passes the selector to the toggles.

Using the show.bs.tab does allow to prevent the switching but requires to handle things differently for tabs than clicks on other buttons/links, which I'd be keen to avoid if I could. And unfortunately, I can't register my listener on window to intercept the event earlier.

Would there be a way for EventHandler not to use the capture phase (I'm guessing there's a reason it's here, though)? This would give more room for the page's code to intercept an manipulate events before Bootstrap handles them.

Reduced test cases

On the tabs documentation, running the following script in the console does not prevent clicks on the tabs in the example from switching:

document.addEventListener('click', e => {console.log('Preventing'), e.preventDefault();e.stopImmediatePropagation();}, true)

Running it on the window does, given the capture would happen there before Bootstrap's listener.

window.addEventListener('click', e => {console.log('Preventing'), e.preventDefault();e.stopImmediatePropagation();}, true)

What operating system(s) are you seeing the problem on?

macOS

What browser(s) are you seeing the problem on?

Firefox

What version of Bootstrap are you using?

5.1, 5.0

mwaibel-go commented 1 year ago

Describe the issue

I stumbled upon this when dealing with the Collapse plugin. The collapse event handlers, too, are added with useCapture = true. For me this causes problems for buttons in card headers. In my case clicking the card header collapses card body, and there is no way for me to prevent clicks on the button from toggling the card.

Reduced test case

<section class="card">
  <div class="card-header d-flex"
    data-bs-toggle="collapse" data-bs-target="#collapse-target" aria-expanded="true"
    style="cursor: pointer"
  >
    <h3 class="card-title"> Card Title </h3>
    <button class="btn btn-danger btn-sm ms-auto" onclick="alert('This should not toggle the card')">
      Click me
    </button>
  </div>
  <div class="card-body">
    <div id="collapse-target" class="collapse show">
      Collapsed content
    </div>
  </div>
</section>

https://jsfiddle.net/gehxa4s5/

What operating system(s) are you seeing the problem on?

Windows 10

What browser(s) are you seeing the problem on?

Chrome 108.0.5359.125 (Official Build) (64-Bit)

What version of Bootstrap are you using?

5.2

nwalters512 commented 2 months ago

I'm running into exactly this same thing when migrating from Bootstrap 4 to 5, also in the context of trying to make sure that clicking buttons in a collapsible row doesn't also collapse it.

I spent some time looking into this. There's at least one somewhat valid use case for handling events in the capture phase: the Esc key in dropdowns and such. Because those events are handled at the document level via event delegation, there's a desire to avoid Esc bubbling to parent elements. I assume this is for things like dropdowns inside of modals.

However, I'm inclined to say that things like that should be the exception, not the rule. That is: default to handle during bubbling, but judiciously use the capture phase when it's specifically necessary.