Svelte: Plugin svelte: A11y: on:blur must be used instead of on:change, unless absolutely necessary and it causes no negative consequences for keyboard only or screen reader users.

Created on 31 May 2020  Â·  13Comments  Â·  Source: sveltejs/svelte

I recently updated Svelte to latest version and this warning started to show up.

So I decided to try that out, change the on:change on some selects to on:blur, but they stopped working. It's only after I click away from the select that the event gets triggered, simply selecting the option doesn't do it.

I know I can simply ignore these, but I wanted to see if I can actually go ahead with this recommendation, maybe I'm doing something wrong. Thanks!

a11y compiler warning question

Most helpful comment

I disabled it by putting a comment one line above the warning:

<!-- svelte-ignore a11y-no-onchange -->
<select  on:change={(e) => alert('hallo')}>
    <option>Item 1</option>
    <option>Item 2</option>
</select>

Edit: added select to example

All 13 comments

My understanding is that's the behavior change you described is actually the entire point of using on:blur as opposed to on:change.

If the user is accessing the select control with a keyboard, then as they go through the options in the select menu, on:change will be fired for each one. But if you use blur, then you only look at the value of the select once it has closed, so you aren't taking action against intermediate values that the user didn't want to select.

I'm not an a11y expert, but I think so long as you don't do anything unreasonable in your change handler, like making large alterations to the page layout, triggering a bunch of network queries, or anything that might cause the select menu to close, then you're just fine continuing to use on:change.

I didn't express myself correctly. I didn't mean to say that the event doesn't trigger as the user goes through the options of the select. I meant that, on a mouse interaction, even after selecting an option, an the option list goes away, still the blur event doesn't get triggered, because the select is still focused I guess. Only after clicking away on some whitespace I see it getting triggered, I can't allow for that sort of ux.

Ah, I see. Here's the official (as far as I've been able to find) rule that the Svelte one was adapted from: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-onchange.md.

Really, I think there's frustratingly little guidance on this rule. It's a big change from what most web developers currently do and the description of the rule is extremely vague on when it's ok to use onchange or not if you aren't already an accessibility expert. The link I posted above has two article links, one of which is dead and the other which is 15 years old: http://www.themaninblue.com/writing/perspective/2004/10/19/

That article is basically is making the case against doing what I mentioned in the last paragraph of my previous comment. This was common back in 2004 when select menus were often used as a kind of simple dropdown navigation, and selecting an option would trigger a page change. But I think that kind of behavior is pretty rare today though.

So I would just sum this one up as "make sure your select's on:change handler is friendly to keyboard users" and keep on using on:change. I think that's what I'm going to do.

There's a webaim.org mailing thread that discusses this issue: https://webaim.org/discussion/mail_thread?thread=8036

Quoting the reply:

In most cases [using onChange] is actually ok.
There are 3 scenarios where this is absolutely a problem (and defect
under WCAG 3.2.2)

  1. If the onchange event automatically moves focus to somewhere else
    on the page.
    This is a problem, because when you are in a dropdown and press the
    arrow key the onchange event is triggered. Then your focus may land
    somewhere else. If you are a keyboard only user and wanted to select
    the 6th option in the dropdown it could take you a long time if you
    have to refocus the dropdown for every arrow down press.
  2. If the onchange event triggers navigating to a different page or
    opening of another user agent (like auto playing a video or displaying
    a PDF file) .. I have never seen this except in theoretical scenarios ,
    but it could be coded that way.
  3. If the onchange event changes content that is located, in content
    order, before the dropdown. This could be remediated in some cases
    using an ARIA live region, but it is usually just a bad idea.
    If your interaction with a control changes the content, the change
    should occur after the control, it is commonsensical.

How do I disable this warning.... on:blur does not work for my use case. It is annoying to see on Visual Studio Code.

You should be able to use rollup's onwarn hook to suppress it: https://rollupjs.org/guide/en/#onwarn

Not sure about other bundlers but there's probably something similar.

How would you ignore this warning if you are running Webpack?

Something I just realized while interacting with this warning — wouldn't this imply that bind:value is also inadvertently breaking the a11y rule? I was helping someone "fix" this warning and our answer was to swap to using bind:value instead (which was the better choice anyway/more Svelte-y). But it did get me thinking about what it was doing behind the scenes and sure enough — it's a change listener!

image

How do I disable this warning.... on:blur does not work for my use case. It is annoying to see on Visual Studio Code.

Edit your rollup.config.js like this. This will give you the opportunity to add more warnings later on.

const onwarn = (warning, onwarn) => {
    const isCircularWarning = warning.code === 'CIRCULAR_DEPENDENCY' && /[/\\]@sapper[/\\]/.test(warning.message); // only for @sapper
    const isOnBlurInsteadOfOnChangeWarning = warning.code === 'PLUGIN_WARNING' && warning.pluginCode && warning.pluginCode === 'a11y-no-onchange';

    return isCircularWarning
        || isOnBlurInsteadOfOnChangeWarning
        || onwarn(warning);
}


export default {
    client: {
          ...
          onwarn,
        },
       server: {
         ..., 
         onwarn,
       }
}

How would you ignore this warning if you are running Webpack?

For webpack it's very similar to the solution given by @dennis-f. In the options for svelte-loader add a new onwarn, like so:

// [...]

rules: [
    {
        test: /\.(svelte|html)$/,
        use: {
            loader: 'svelte-loader',
            options: {
                dev,
                hydratable: true,
                hotReload: false, // pending https://github.com/sveltejs/svelte/issues/2377
                onwarn: function (warning, handleWarning) {

                    if (warning.code === 'a11y-no-onchange') { return }

                    // process as usual 

                    handleWarning(warning);
                }
            }
        }
    },
    {
        // some other loader here...
    }
],

// [...]

This option is undocumented in https://github.com/sveltejs/svelte-loader, but it's easy to find it by looking at the source.

Something I just realized while interacting with this warning — wouldn't this imply that bind:value is also inadvertently breaking the a11y rule? I was helping someone "fix" this warning and our answer was to swap to using bind:value instead (which was the better choice anyway/more Svelte-y). But it did get me thinking about what it was doing behind the scenes and sure enough — it's a change listener!

image

The idea isn't that change should never be listened to. It's that if you _do_ listen to it, you should ensure that the event handler doesn't do anything that would cause the select control to be unusable for a keyboard user (since the change event will fire for each arrow key press as they move through the options, unlike for a mouse user where it doesn't fire until they click on their chosen option). For example, if the change handler can cause the select control to lose focus, or be removed from the document, then this could make the control unusable for a keyboard user.

I think it's probably reasonable for Svelte to push this responsibility onto the developer even when using bind:value, although it might be worth a warning so that people are aware that this is something to think about. The alternative (using on:blur instead of on:change internally for bind:value) would not work correctly for a lot of use cases, because a second click to defocus the select control would be required for the value to update.

The a11y warning could be worded better to make it clear that listening to change can be absolutely fine (and in fact is often the desired event), as long as you give consideration to these issues.

I disabled it by putting a comment one line above the warning:

<!-- svelte-ignore a11y-no-onchange -->
<select  on:change={(e) => alert('hallo')}>
    <option>Item 1</option>
    <option>Item 2</option>
</select>

Edit: added select to example

Thanks, this warning is way over the top. It's absolutely untrue that it doesn't change anything for desktop.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sskyy picture sskyy  Â·  3Comments

Rich-Harris picture Rich-Harris  Â·  3Comments

juniorsd picture juniorsd  Â·  3Comments

ricardobeat picture ricardobeat  Â·  3Comments

bestguy picture bestguy  Â·  3Comments