javascriptcssreactjs

Apply :focus-visible when focusing element programatically


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>


Solution

  • Not 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.