javascriptdialogmodal-dialogfocuspreventdefault

I'm not able to find what e.preventDefault() does in trapFocus function


I understand what the function does, but I don't really know what does e.preventDefault() do. I know that prevents the default behaviour from the keydown event, but I don't really know which behaviour is that, I haven't been able to find it.

Reference article: https://hidde.blog/using-javascript-to-trap-focus-in-an-element/

Trap focus function

trapFocus(document.getElementById('dialog-backdrop'));

function trapFocus(element) {
  const focusableLms = element.querySelectorAll('a[href]:not([disabled]),button:not([disabled]),textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]),input[type="checkbox"]:not([disabled]), select:not([disabled])');
  const firstFocusableLm = focusableLms[0];
  const lastFocusableLm = focusableLms[focusableLms.length - 1];

  element.addEventListener('keydown', (e) => {
    const isTabPressed = (e.key === 'Tab');

    if (!isTabPressed) {
      return;
    }

    if (e.shiftKey) /* shift + tab */ {
      if (document.activeElement === firstFocusableLm) {
        lastFocusableLm.focus();
        e.preventDefault();
      }
    } else /* tab */ {
      if (document.activeElement === lastFocusableLm) {
        firstFocusableLm.focus();
        e.preventDefault();
      }
    }
  });
}
<div id="dialog-backdrop" class="dialog-backdrop">
  <div class="alert-dialog" id="alert-dialog" role="alertdialog" aria-label="Confirm discard changes." aria-describedby="alert-dialog__desc">
    <img src="img/garbage-collector-2.jpg" alt="" />
    <button aria-label="Close dialog." type="button" class="alert-dialog__cancel-btn" id="alert-dialog__cancel-btn">
        <span aria-hidden="true" class="material-symbols-outlined">cancel</span>
      </button>
    <p class="alert-dialog__desc" id="alert-dialog__desc">Are you sure you want to discard all changes made in form?</p>
    <div>
      <button id="alert-dialog__confirmation-btn" type="button">Yes</button>
      <button id="alert-dialog__discard-btn" type="button">No</button>
    </div>
  </div>
</div>

If I remove it, it only traps focus for two of the three button elements in the dialog. It completely ignores the close button. And only cycles between the yes and no button.


Solution

  • The default behavior for pressing the tab key is to move to the next focusable HTML element. In the case of the code:

    if (document.activeElement === lastFocusableLm) {
      firstFocusableLm.focus();
      e.preventDefault();
    }
    

    If the new/next focusable element is the lastFocusableLm the focus will change to the firstFocusableLm. If you leave out e.preventDefault(); the default behaviour will be applied -- that is, moving focus to the next element -- that is the element after firstFocusableLm. That's why the issue happens, it ignores the firstFocusableLm aka. the close button. It just jumps twice.

    Calling e.preventDefault(); will stop the keyup event and therefore it will not continue with the action of focusing the next element.