-
Notifications
You must be signed in to change notification settings - Fork 124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix issue number #33 #34
Conversation
…o the target if the user navigates to it via Tab or Shift + Tab 2. Remove use of data-focus-ring-added 3. Remove need for use of matches polyfill 4. Remove need for setTimeout()
Mostly looks good, but I'd like to get at least @bkardell @robdodson or @marcysutton to take a look before we merge, since this is a big behaviour change. |
src/focus-ring.js
Outdated
return; | ||
|
||
var isTabKey = e.keyCode == 9; | ||
var isTabNavigation = isTabKey || isTabKey && e.shiftKey; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This reduces to just isTabKey, doesn't it? Perhaps a comment instead noting that the shift key may or may not be pressed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does indeed. Previous approach was more out of the desire to have the code be explicit & self-documenting about when we match. But if you don't like it, then consider it changed.
src/focus-ring.js
Outdated
addFocusRingClass(document.activeElement); | ||
if (keyboardThrottleTimeoutID !== 0) | ||
clearTimeout(keyboardThrottleTimeoutID); | ||
keyboardThrottleTimeoutID = setTimeout(function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm how do you feel about the case where, say, using Enter
to activate a button causes a UI change? Should :focus-ring
match then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't adding a focus ring superfluous in that case as the UI change alone is an indicator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it depends what ends up being focused. If it's a widget you might immediately want to interact with, I think it might be disorienting to hide the focus ring. Using enter to activate a button is a stronger signal than using a keyboard shortcut.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely clear on the use case you are advocating for. Would you mind explaining in more detail? Perhaps with a real-life example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a real life example, unfortunately, but I'm imagining something like a custom date picker which pops up some extra UI when you press a button.
Another thought: this also means :focus-ring
wouldn't match for UI which supports directional navigation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't directional navigation in the same category as a screen reader's virtual navigation, and therefore the visual indicator is provided by the host service (e.g. like VoiceOver's black outline).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean author-implemented directional navigation as opposed to "spatial navigation" as a UA feature using some kind of D-pad.
The example I just saw was in Google Slides, where you go to add a new slide and you get a (in my case at least) 3x4 grid of slide layout options that you can choose between.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not all keyboard users are also using AT, if you had a menu of menu items for example you want them to show the ring, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bkardell wouldn't a focus ring be superfluous for menu items? Consider the menubar in Mac OS—you don't get both the selection highlight + a focus ring when navigating via the up & down arrow keys, you simply see the selection state change (blue background).
I don't know how we could account for all author-defined directional navigation while at the same time not matching too aggressively (as we are now). Hence my comment in issue #33 indicating we should be more conservative with this polyfill and allow authors to decide when to show the focus ring when they are implementing author-defined keyboard shortcuts or navigation.
For example, take your menu use case. Any menu I have ever implemented with keyboard accessibility has code that looks like this:
function onKeyDown(e) {
if (e.keyCode == 40) {
$(e.target).next().focus();
$(e.target).next().addClass('selected');
}
}
In such cases I'm already explicitly managing both where focus should move and what it should look like via adding a class—so I can match the selection style also used by the mouse. So, having a focus-ring
class also applied in this case wouldn't be desirable to me. In fact, it would be duplicating efforts.
src/focus-ring.js
Outdated
* @param {Event} e | ||
*/ | ||
function onFocus(e) { | ||
if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) | ||
addFocusRingClass(e.target); | ||
hadKeyboardEvent = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this weirdly indented or is it meant to be part of the if
block?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
always use {} and save yourself the hassle is my motto :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bkardell agreed. All {}
all the time. Done. Only didn't previously as previously the code didn't use them and I thought that may have been the preferred style by @alice or @robdodson.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For single lines I'm in the habit of avoiding them since that's the style we use in Chromium. But you have to be used to that in order to be used to adding them for additional lines (since not adding them can create bugs along the lines of GOTOFAIL)
src/focus-ring.js
Outdated
* - a keyboard event happened in the past 100ms, or | ||
* - the focus event target triggers "keyboard modality" and should always | ||
* have a focus ring drawn. | ||
* - a keydown event preceded the focus event, meaning the target received |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: remove everything up to and including "meaning"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -265,7 +225,6 @@ document.addEventListener('DOMContentLoaded', function() { | |||
if (index(el).contains('focus-ring')) | |||
return; | |||
index(el).add('focus-ring'); | |||
el.setAttribute('data-focus-ring-added', ''); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, why is this being removed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I couldn't work out why it was necessary in the first place. Isn't it superfluous? Doesn't the presence of the focus-ring
class give us enough?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It disambiguates an explicit, author-added focus-ring
class (to allow always matching .focus-ring
) from a programmatically added one. See line 281 for where it's used. It was added in 8d00580
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's a use case for an author-added focus-ring
class? And why would we want to support that if this is a polyfill for :focus-ring
and there would be no similar pattern once this hopefully is implemented as a pseudo selector in browsers. Wouldn't we want to discourage authors adding focus-ring
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check out the change for context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain how this is more 'with-the-grain' of the eventual pseudo? I'm not following.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In any case, this is orthogonal to the main point of this PR, so maybe let's revert it here and continue the discussion on a bug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bkardell I don't think this polyfill should give authors abilities they won't have with the actual :focus-ring
psuedo. Therefore, once actual support for :focus-ring
lands in browsers the only change authors need to make to their CSS is changing their rules from .focus-ring
to :focus-ring
. Hence, more with the grain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we file a bug for this discussion and revert this change in the context of this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alice bug is filed. And this PR has been updated to restore data-focus-ring-added
.
src/focus-ring.js
Outdated
@@ -52,8 +52,10 @@ document.addEventListener('DOMContentLoaded', function() { | |||
* @param {Element} el | |||
*/ | |||
function addFocusRingClass(el) { | |||
if (classList(el).contains('focus-ring')) | |||
if (classList(el).contains('focus-ring')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we please revert these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...and done.
@@ -278,42 +237,49 @@ document.addEventListener('DOMContentLoaded', function() { | |||
* @param {Element} el | |||
*/ | |||
function removeFocusRingClass(el) { | |||
if (!el.hasAttribute('data-focus-ring-added')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your commit to add these back in, but for some reason the diff is showing these are still removed.
Edit: Ah, I see it's on the dist version of the file. I wonder if git should exclude the dist files and generate them with build scripts instead? Makes reviewing easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcysutton Sorry about that! I forgot to re-build the dist. Fixed now.
src/focus-ring.js
Outdated
@@ -72,31 +76,37 @@ document.addEventListener('DOMContentLoaded', function() { | |||
* are no further keyboard events. The 100ms throttle handles cases where |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If throttling is going away this comment should probably be updated, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcysutton nice eagle eye. Thank you! Fixed.
dist/focus-ring.js
Outdated
return; | ||
|
||
var isTabKey = e.keyCode == 9; | ||
var isTabNavigation = isTabKey || isTabKey && e.shiftKey; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems a bit of a weird and'ing and or'ing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bkardell it is right in the source code, I just forgot to re-build the dist. Will do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bkardell fixed now.
Initially I wasn't sure if suppressing focus on keyboard shortcuts was a good idea, but you can still override the focus style using |
@alice wanted to check in on this PR and if you've got any outstanding concerns that need to be addressed. @marcysutton and @bkardell have reviewed and I've addressed their issues. (@marcysutton, @bkardell either of you have any additional comments, or are you good?) I'm still waiting on a review from @robdodson. |
Apologies, I've been in meetings all day. Yeah, would like to get @robdodson's 2c but he's out till next week. |
Can you expand on how this plays into the context of whether focus has come from tab/shift tab or not? |
@alice I think @marcysutton's comment with respect to "suppressing focus on keyboard shortcuts" was related to the thread you, @bkardell and I had going yesterday with respect to author-implemented directional navigation. |
Yes, it was. With this version of the polyfill, tab/shift+tab worked as I expected and the keyboard shortcut case was still achievable with :focus. |
Sure, the part I don't follow is how the |
Here's a demo–I got a button to show a focus style after manually sending focus there via keyboard shortcut by using a combination of |
Right - that button also gets a focus ring if you just click it. So the part I'm wondering about is how we handle the case where we do want the button to get a focus ring on keyboard shortcuts, without reverting to simply So perhaps it's just as simple as applying an extra class to the |
Oh yeah, duh! This doesn't actually work then. And it highlights another issue–what if I want to show the focus style for focus management? I use that pattern often to show keyboard users where they are on the screen after sending focus to a new area. That style is suppressed with this change. |
@alice @marcysutton I have to admit we're so far into this thread I've lost the original context with respect to:
So, my apologies for not following along with you both. If @marcysutton's concern is how to indicate focus when the user switches contexts (e.g. into a dialog, into a menu, or a side panel), I was under the impression @alice had already addressed that in her summary for why the recommendation to UAs is
@alice's comment is inline with my experience with any webapp I've ever worked on; the code managing a context switch—especially when moving to a dialog or menu—is also explicitly setting focus programmatically to the next control representing the next action the user is likely to take, and also applying some class to draw attention to said control. In the case of a dialog this is most often either:
In either of these use case, authors can leverage the cascade to ensure the focus ring is visible in response to programatic focus following a context switch:
In the case of a popup menu I've never had a need to render a focus outline as the selection change made via keyboard navigation is always intended to render to match the selection state on mouseover. Anyway, sorry for the long message. I hope this resolves this issue (presuming this is the issue you are actually talking about). |
The reason I bring up the focus management use case is that it's impacted by this change. Sadly, I wouldn't use The problem I hoped For example, this code persists the focus style on click if the element is a custom control with
Custom controls aren't ideal, I know, but when they happen in the real world and persist on click, that's when focus styles get disabled entirely. Whether you move focus to a wrapper element with Here are the various use cases I would hope this polyfill could cover:
This isn't an easy one to solve, so my apologies if this feels like piling on. But it was important for me to go back and remember my motivations for a tool like :focus-ring in the first place. Hopefully these details help bring that all into...focus. (I'll show myself out. :)) |
@marcysutton the custom control issue you call out is a legit problem, but that's just a bug and (IMO) orthogonal to the issue of the heuristics for the keyboard behavior we should match on. I'll open an issue and PR to address that... one moment. |
@marcysutton actually, looking at the code and testing the demo we're already supporting custom controls save those with a |
@kloots how? When I click on a custom element with tabindex="0", the focus ring shows. I want to make sure I didn't misunderstand the usage API. |
@marcysutton ohhhh that! Yes, that is a bug. In fact, I believe I have fixed that in a separate PR. |
@marcysutton I take it back, I cannot repro your bug in the demo on master or on this branch. Try clicking the |
…listener such that it no longer calls addFocusRingClass()
👍 So sorry for letting this go cold! |
@kloots do you think this is ready to merge? If so let's 🚀 |
@robdodson It's ready to merge now. Just added one more commit from my fork which prevents handling |
Just started reviewing :) |
I wanted to double check one last thing based on @marcysutton's comments. In this example snippet you provided:
The |
@robdodson yes, expected. In that instance the context (if I remember) was a focus style for a button that mapped to the default action for a dialog, and as such, the focus style should always be visible regardless of how the button received focus: programmatic (on initial display of the dialog) or via user interaction (via keyboard or mouse). |
@kloots ok. Just curious, do you have an example of using this behavior in Slack? I just wanted to see it in action if possible. |
@robdodson no example of that behavior in Slack (at least I don't think). But I think this specific point is outside the scope of this PR and is the reason why @alice started a separate discussion for this in issue #42, no? Or is this blocking the merging of this PR? |
I think it's blocking me because I'm starting to wonder if a one-size-fits-all :focus-ring style, by itself, is workable. Seeing Slack and Twitter fork it so they can add specific behavior is really interesting to me and makes me wonder if we need to add in the power of #42 at the same time. Otherwise it seems like someone will always be unhappy. As @marcysutton stated, I think she prefers the way it currently works and if we made this change she would switch back to WhatInput. |
I say let's go ahead and merge (and apologies again for how long that took) - but definitely stay open to feedback from people who feel this change breaks things. |
This PR addresses issue #33 by refining the heuristics for when the focus-ring class is applied: if the user navigates to it via
Tab
orShift
+Tab
.In addition, this PR also:
data-focus-ring-added
attributedom-matches
polyfill dependencysetTimeout()
when handling thekeydown
event.