I want to apply styling to my element when I'm focus it using my keyboard or when focused programatically using HTMLElement.focus()
.
E.g in the example below. I want the <div>
to have blue borders initially or when I focus with tab. I only want it to have red border when I click the <div>
using my mouse.
Is this possible?
EDIT1: Changed from triggering focus with a button to just on render as this scenario wasn't accurate to my actual issue.
EDIT2: Changed from vanilla JS to React as I though that might be the issue. Seems like it still works like I want to to here, but not in my App :(. I have no clue why.
const useFocusOnMount = (ref) => {
React.useEffect(() => {
if (ref.current) {
ref.current.focus();
}
}, [ref]);
};
const App = (props) => {
const divRef = React.useRef(null)
useFocusOnMount(divRef);
return (
<div ref={divRef} role="listbox" tabindex="0" className="focusable">
<div data-testid="displayValue">Lol</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
.focusable {
border: 1px solid lightgrey;
outline: none;
}
.focusable:focus {
border: 2px solid red;
}
.focusable:focus-visible {
border: 2px solid blue;
}
<script src=".8.0/umd/react.production.min.js"></script>
<script src=".8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I want to apply styling to my element when I'm focus it using my keyboard or when focused programatically using HTMLElement.focus()
.
E.g in the example below. I want the <div>
to have blue borders initially or when I focus with tab. I only want it to have red border when I click the <div>
using my mouse.
Is this possible?
EDIT1: Changed from triggering focus with a button to just on render as this scenario wasn't accurate to my actual issue.
EDIT2: Changed from vanilla JS to React as I though that might be the issue. Seems like it still works like I want to to here, but not in my App :(. I have no clue why.
const useFocusOnMount = (ref) => {
React.useEffect(() => {
if (ref.current) {
ref.current.focus();
}
}, [ref]);
};
const App = (props) => {
const divRef = React.useRef(null)
useFocusOnMount(divRef);
return (
<div ref={divRef} role="listbox" tabindex="0" className="focusable">
<div data-testid="displayValue">Lol</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
.focusable {
border: 1px solid lightgrey;
outline: none;
}
.focusable:focus {
border: 2px solid red;
}
.focusable:focus-visible {
border: 2px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Share
Improve this question
edited Sep 22, 2021 at 10:09
mTv
asked Sep 22, 2021 at 9:27
mTvmTv
1,3564 gold badges22 silver badges40 bronze badges
2
- 1 It does have a blue border when using keyboard and a red border when clicking with a mouse? – evolutionxbox Commented Sep 22, 2021 at 9:31
- I want it to get a blue border even when clicking the button with my mouse. In my real world case i dont use a button to trigger the focus(), so maybe this was a bad example. I'll change it – mTv Commented Sep 22, 2021 at 9:35
4 Answers
Reset to default 10Not sure why you want this, and I would advise you to find an other way to do what you want (e.g, why not use only :focus
?).
But for the hack, you can trick the browser in thinking your div is editable, it should* make it trigger its focus-visible
rule.
const focusable = document.getElementById('focusable');
const button = document.getElementById('button');
const onClickHandler = (e) => {
focusable.contentEditable = true;
focusable.focus();
focusable.contentEditable = false;
};
button.addEventListener("click", onClickHandler);
.focusable {
border: 1px solid lightgrey;
outline: none;
}
.focusable:focus {
border: 2px solid red;
}
.focusable:focus-visible {
border: 2px solid blue;
}
<div id="focusable" class="focusable" tabIndex=0>Lol</div>
<button id="button">Focus Lol</button>
*I only tested in latest Chrome and Firefox.
Conceptually, this is a very basic request. Focus-visible gives us the power to create more prominent focus indicators for mouse-free users who most require them, while hiding them from users who don't. If you dismiss a modal with the escape key, to use a common programmatic example, you might want to see a focus ring, whereas if you clicked a close button, you might not.
Please note that in most scenarios, the CSS :focus-visible pseudo selector is quite sensitive to programmatic focus immediately following a key press. The example of dismissing a modal is one of the few that might require some custom script since multiple fetches might need to occur before the element to receive focus has rendered.
I created this Codepen to illustrate the custom script required in that scenario.
The heavy lifting is this method:
// our method to show programatic focus-visible
function showProgrammaticFocusVisible(container){
let focusedViaMouseAttributeName = 'data-focused-via-mouse';
let navigatingViaMouse = false;
container.addEventListener('click', function(e){
navigatingViaMoused = true;
// wait for next thread, after focusin event
setTimeout(function(){
navigatingViaMouse = false;
})
});
container.addEventListener('focusin', function(e){
// wait for next thread, after click event
setTimeout(function(){
if(navigatingViaMouse){
e.target.dataset.focusedViaMouse = 'true';
} else {
e.target.dataset.focusedViaMouse = 'false';
}
})
});
container.addEventListener('focusout', function(e){
e.target.dataset.focusedViaMouse = '';
});
}
In the codepen, I chose to limit the programmatic focus-visible behavior to a specific class, but it could be implemented more broadly.
:focus-visible,
.show-focus-visible[focused-via-mouse=false] {
outline: solid 2px blue;
outline-offset: 2px;
}
.show-focus-visible[focused-via-mouse=true] {
outline: 0;
}
There's now an experimental feature to do that:
myElement.focus({ focusVisible: true });
cf https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#focusvisible
Sadly, for now, it's only available on Firefox... https://caniuse.com/mdn-api_htmlelement_focus_options_focusvisible_parameter
You can't unfocus elements if you point to the element itself.
You must style element with an event listener on body, because outside
click is on body, not on the element itself.
You can use e
like this
to spot where users click.
If e
(click) !== (is different to/not on) focs
(element) outline = 0.
const focs=document.getElementById('focs');
const focsb=document.getElementById('focsb');
document.body.addEventListener('click',function(e){
if(e.target!==focs&&e.target!==focsb) {
focs.style="outline: 0";
} else if (e.target==focs&&e.target!==focsb) {
focs.style="outline: 2px solid #000";
} else {
focs.style="outline: 2px solid #CC0000";
};
});
<input id="focs" type="text">
<button id="focsb">Focus</button>