bootstrap: bootstrap modal enforceFocus() is fundamentally not accessible and not modular / incompatible with Select2

Summary bootstrap modal component has a pernicious enforceFocus() helper that interferes with other plugins, such as Select2. There has to be a more accessible way to achieve the goals set out by this helper function than to constantly steal focus.

Proposed Solution I propose bootstrap modal needs a distinct model of the UI in order to handle events in an accessible and modular style, otherwise it will fundamentally be incompatible with other UI components in unpredictable ways.

Observed Behavior When hosting a select2 widget in a bootstrap4 modal, select2 works correctly in IE but does not work in Chrome.

Troubleshooting After digging into the code, it became clear that Chrome was functioning “correctly” and the real problem was in bootstrap modal’s use of an enforceFocus() helper. select2 controls inside the modal try to get focus, and the modal outer component steals it away. This is due to the fact select2 is attached directly to the body element [1] , which is the most logical place for third-party controls (e.g. select2) to cache DOM nodes while knowing nothing about the app consuming its behavior. Since select2’s DOM nodes are not nested inside the modal but transposed on top via fancy logic, enforceFocus() thinks the user is performing some action outside the modal.

[1] https://github.com/select2/select2/blob/4.0.2/src/js/select2/dropdown/attachBody.js

Test Details:

  • IE 11.0.9600.18314 w/ 11.0.31 (KB3154070) update
  • Chrome 50.0.2661.102 m

Example http://jsbin.com/viwizoyofe/2/edit?html,js,output

Example Walk-through Explanation

  1. Go to the Output pane.
  2. Click the dropdown that says red.
  3. Observe that, since bootstrap does not yet see this dropdown as inside a modal dialog, auto-complete inside the control works fine.
  4. Click “Convert to popup”, which will trigger bootstrap to see this dropdown as inside a modal dialog.
  5. Click the dropdown that says red, again.
  6. Observe that, since bootstrap sees this dropdown as inside the modal dialog, auto-complete stops working.

When something tries to set focus on anything that is outside of the modal dialog, Bootstrap Modal will return focus to the whole dialog. Incidentally, the dropdown that Select2 creates is outside of the modal dialog (appended to <body>), including the . When you open the dropdown, Select2 tries to set focus to the , and Bootstrap Modal immediately returns focus to the dialog, leaving focus-less and unable to receive keystrokes.

This autocomplete works before you click the link once, because at that point there is no modal dialog. The element itself exists, but Bootstrap Modal doesn’t see it as a modal dialog yet.

This complete example shows that there is nothing wrong with select2 itself, and the problem resides solely in Bootstrap Modal stealing focus. This can be further verified using the following Chrome Debugger steps:

  1. Click the “Convert to Popup” link once.
  2. Open Dev Tools -> Sourcs -> bootstrap.js -> search for _enforceFocus, set a breakpoint on the innermost statement (for me its line 1910).
  3. Click Select2, see the breakpoint hit.
  4. Resume execution
  5. Click the within the Select2 dropdown, see the breakpoint hit again.

I’m not 100% sure why IE11 allows Select2 to work within Bootstrap Modal, but it could be that IE11 doesn’t let you set focus from within a “focus changed” event handler or some such. The dangers of an infinite loop when doing so are even attempted to be handled by Bootstrap Modal, with the following code:

  key: '_enforceFocus',
  value: function _enforceFocus() {
    var _this9 = this;

    $(document).off(Event.FOCUSIN) // guard against infinite focus loop
    .on(Event.FOCUSIN, function (event) {
      if (_this9._element !== event.target && !$(_this9._element).has(event.target).length) {
        _this9._element.focus();
      }
    });
  }

Proposed Solution It would probably be best if we just removed the enforceFocus() helper altogether. There don’t seem to be any unit tests that actually test this behavior [1] and it’s impossible to the naive consumer to predict which components will work well with it. Even more troubling, IE [11.0.9600.18314 w/ 11.0.31 (KB3154070) update] does not respect enforceFocus() and so it gives the appearance to the client user, comparing IE and Chrome, that there is a “bug” in Chrome.

[1] https://github.com/twbs/bootstrap/blob/v4-dev/js/tests/unit/modal.js

Workaround I ended up just using tether directly and writing my own TypeScript-based PopupViewModel that attaches to document.body. But this is a lot of work to be developing/maintaining my own proprietary popup.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 16 (2 by maintainers)

Most upvoted comments

The option focus exists, and fulfill exactly that purpose. See modal documentation.. By default it’s true. If set to false _enforceFocus will never be called for this modal.

Yep, I had same problem today: using select2 inside a bootstrap modal. Digging on Google I found your jsfiddle and your code solved my problem (with a litle adjustment). So I posted It in the topic and in a Stack overflow issue.

Thanks, you helped me a Lot.

Erm, we can’t remove enforceFocus, at least not without replacing it with something similar. (a11y.js has been suggested, but it’s similarly magical/intrusive.) It’s what actually makes the modal be modal for non-mouse users. Without it, users could <kbd>Tab</kbd> outside of the modal and start interacting with other parts of the page. What I think we need is some better way of allowing users to override the “is this ‘within’ the modal?” logic and/or a way for users to mark their components as having their own focus management.

There has to be a more accessible way to achieve the goals set out by this helper function than to constantly steal focus.

Such as? Also, you seem to be describing interoperability problems rather than accessibility problems per se.

I propose bootstrap modal needs a distinct model of the UI in order to handle events in an accessible and modular style,

Please explain what you mean by “a distinct model of the UI”.