preline icon indicating copy to clipboard operation
preline copied to clipboard

Sveltekit + Preline modal tab order is incorrect

Open fortserious opened this issue 1 year ago • 2 comments

Summary

When using a modal in a Preline + SvelteKit + TailwindCSS project set up as instructed by documentation, modal tab order is incorrect.

Steps to Reproduce

  1. Create new Sveltekit + Tailwind project per https://tailwindcss.com/docs/guides/sveltekit
  2. Install Preline with SvelteKit configurations per https://preline.co/docs/frameworks-svelte.html
  3. Install overlay and forms
  4. Create basic modal per https://preline.co/docs/modal.html
  5. Create basic input per https://preline.co/docs/input.html (with or without tabindex="0")

The example has been provided with focusable elements, however taborder is incorrect even without them.

If there are no focusable elements in the modal, then tab will only move to the Close button, with no ability to tab to the Close icon.

I believe the problem has something to do with these lines from preline.js:

onTab:

e.onTab = function(t, e) { if (!e.length) return !1; var n = t.element.overlay.querySelector(":focus") , o = Array.from(e).indexOf(n); o > -1 ? e[(o + 1) % e.length].focus() : e[0].focus() }

autoInit:

(e.autoInit = function () { window.$hsOverlayCollection || (window.$hsOverlayCollection = []), document .querySelectorAll('[data-hs-overlay]:not(.--prevent-on-load-init)') .forEach(function (t) { window.$hsOverlayCollection.find(function (e) { var n; return ( (null === (n = null == e ? void 0 : e.element) || void 0 === n ? void 0 : n.el) === t ); }) || new e(t); }), window.$hsOverlayCollection && document.addEventListener('keydown', function (t) { return e.accessibility(t); }); }),

In my tests, this function runs successfully initially for the element with the :focus pseudo-class and the correct index is returned, however, the document keydown event is initiated three times:

  1. From preline itself
  2. From the 'goto' event from afterNavigate afterNavigate((result)=>console.log(result.type)
  3. From the 'enter' event from afterNavigate afterNavigate((result)=>console.log(result.type)

Removing the call to window.HSStaticMethods.autoInit(); in afterNavigate results in modals not working, and a call to either breaks the tab order again, so I haven't been able to develop a good workaround (short of removing all focusable elements in a modal besides the action buttons, which isn't an acceptable solution).

Please let me know if there is any other info I can provide to help nail this down!

Demo Link

https://stackblitz.com/~/github.com/fortserious/sveltekit-preline-modal-accessibility-bug

Expected Behavior

Tabbing through inputs proceeds in DOM order: Close (Icon), Input 1, Input 2, Close, Save Changes.

Actual Behavior

Tabbing through inputs proceeds in unusual order: Input 2, Close (Icon), Close (button), Input 1, Save Changes.

Screenshots

No response

fortserious avatar Sep 06 '24 15:09 fortserious

I can confirm this issue also exists in Vue.js. The Tab key order is reversed within the forms shown in modals. The problem is on this method as @fortserious has pointed already: https://github.com/htmlstreamofficial/preline/blob/0c521a3c454f2ce345cad137473b77a89ed50a33/src/plugins/overlay/index.ts#L496

onurkose avatar Oct 08 '24 10:10 onurkose

I can also confirm that this issue occurs with plain JavaScript when using htmx to load HTML from the server.

I don't believe the tab order is reversed. Instead, it seems that the window.HSStaticMethods.autoInit(); function is called on page load, and because we're dynamically loading additional HTML into the DOM later, we invoke window.HSStaticMethods.autoInit(); again.

https://github.com/htmlstreamofficial/preline/blob/c8f941a113556fb3e13a7c296be012f89c171c35/src/plugins/overlay/index.ts#L638C1-L644C4

If you examine the autoInit() function for the overlay plugin, you'll notice that it registers an event listener.

https://github.com/htmlstreamofficial/preline/blob/c8f941a113556fb3e13a7c296be012f89c171c35/src/plugins/overlay/index.ts#L400C3-L404C4

Since this function runs multiple times, the event listener is registered multiple times. This causes the onTab function to execute multiple times, depending on how often window.HSStaticMethods.autoInit(); is called.

As a result, when I press the Tab key, the function runs twice, causing it to skip over other inputs.

The Fix:

I modified the code by moving the event listener into its own function and removing the listener before adding it again. This resolved the issue for me.

    ....
    if (window.$hsOverlayCollection) {
        document.removeEventListener('keydown', onKeyDown)
	document.addEventListener('keydown', onKeyDown);
    }
}

...

const onKeyDown = (evt: KeyboardEvent) => {
	HSOverlay.accessibility(evt)
}

...

I'm considering creating a pull request to fix this bug and two others. It would be great if you could verify whether this fix also resolves the issue in Svelte and Vue.js.

I have included the dist Preline folder as an attachment if you'd like to try my changes. It also contains fixes for two other bugs: allowing the use of Shift+Tab to navigate to the previous input, and preventing tabindex="-1" elements in overlays from being tab targets. Additionally, there are some console.log statements that I still need to remove.

dist.zip

RobertHalfdanar avatar Oct 17 '24 00:10 RobertHalfdanar

@fortserious Hi, Thank you for your feedback. We will fix this behavior in the next update and inform you here.

olegpix avatar Oct 28 '24 12:10 olegpix

Hey @olegpix

I believe I'm experiencing this issue using Vue. I have a Preline modal component and am finding that the tabbing skips a component inside of the modal. This seems identical to what @RobertHalfdanar has described. Just curious to know when a fix is due. Thanks.

UnlockQA avatar Nov 04 '24 09:11 UnlockQA

@UnlockQA Hi, The update will be out soon, hopefully this week.

olegpix avatar Nov 04 '24 14:11 olegpix

I'm considering creating a pull request to fix this bug and two others.

@RobertHalfdanar My attempt at fixing the underlying issue has been open for a while https://github.com/htmlstreamofficial/preline/pull/441. I did not observe wrong tab order or similar (but never really tested it either), only sluggishness, but I'm pretty sure it's basically all the same issue.

oliverhaas avatar Nov 11 '24 21:11 oliverhaas

Hey everyone, the issue has been resolved in the latest v2.6.0 release. Thanks!

jahaganiev avatar Dec 04 '24 00:12 jahaganiev