'Bootstrap modal focus trap behaving differently on BS documentation page

I'm using Bootstrap modal and it doesn't seem to trap focus inside the modal. However, when tested on Bootstrap webpage https://getbootstrap.com/docs/4.1/components/modal/ this is working as it should i.e. focus is trapped within modal.

I've just used the example code you can find on the BS website. Also, I've seen fixes for this issue but I'm just baffled why it's working in the BS webpage but not when I use the same code in my solution.

Any help is much appreciated.

JS Bin link to the code - https://jsbin.com/zadavagifo/edit?html,output

$('#exampleModalCenter').modal();
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
  <title>JS Bin</title>
</head>

<body>
  <!-- Button trigger modal -->
  <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModalCenter">
  Launch demo modal
</button>

  <!-- Modal -->
  <div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLongTitle">Modal title</h5>
          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
        </div>
        <div class="modal-body">
          ...
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
          <button type="button" class="btn btn-primary">Save changes</button>
        </div>
      </div>
    </div>
  </div>
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
</body>

</html>


Solution 1:[1]

Unfortunately, I have encountered the exact same problem. I cannot find a clean way to do it so the option that worked for me was to force things to work as expected

function handleTrapEvent(lastFocusableElement, firstFocusableElement) {
  return function eventHandler(event) {
    const isTabPressed = event.key === 'Tab' || event.keyCode === 9;

    if (!isTabPressed) {
      return;
    }

    if (event.shiftKey) {
      if (document.activeElement === firstFocusableElement) {
        lastFocusableElement.focus();
        event.preventDefault();
      }
    } else if (document.activeElement === lastFocusableElement) {
      firstFocusableElement.focus();
      event.preventDefault();
    }
  };
}

function addTrapHandler(modal) {
  const focusableElements = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
  const firstFocusableElement = modal.querySelector(focusableElements);
  const focusableContent = modal.querySelectorAll(focusableElements);
  const lastFocusableElement = focusableContent[focusableContent.length - 1];

  document.addEventListener('keydown', handleTrapEvent(lastFocusableElement, firstFocusableElement));
}

function initTrap(modals) {
  modals.forEach((modal) => {
    addTrapHandler(modal);
  });
}

document.addEventListener('DOMContentLoaded', () => {
  const modals = document.querySelectorAll('.modal');
  initTrap(modals);
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 TwistedOwl