DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,503 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Detecting Select All on the Web
Sam Thorogood for Google Web Dev

Posted on

Detecting Select All on the Web

On the web, hitting Cmd/Ctrl-A (or triggering "Select All" another way) has two distinct modes of operation.

If your focus is inside a <textarea>, <input>, or any element marked with contentEditableβ€”that is, typing keys would enter text thereβ€”then Select All will select all of that element's contents. Great! But otherwise...

Problem

🚨 If a user is focused in a non-editable part of your page, they'll instead select all of the page's content.

Select-All in the Twitter interface

And for a user on a site which predominantly features a large editor (like a blog authoring interface!), this might be frustrating. The content of a <textarea> will look πŸ‘€ selected... because in a way, it is, as a byproduct of the whole page being selected, but:

  • typing (or pasting) won't replace the content
  • copy will copy the entire page, including the text of UI elements

Solution

Let's detect when the user triggers Select All, and redirect their selection to an editor of your choice. This solution is already in place on Emojityper. πŸ˜‚βŒ¨οΈ

If you've read my other posts, you might remember that I'm not a huge fan of hooking into keyboard events directlyβ€”there's many ways to trigger Select All, and not all of them are tapping Ctrl/Cmd-A.

So the solution has three steps:

  1. adding two hidden, but selectable elements to your page
  2. listening for the selectionchange event
  3. calling .focus() on your editor.

1. Hidden Elements

Add two hidden elements to your page, which are selectable, but invisible (not display: noneβ€”they need to be on the page), with CSS like:

.extent {
  position: fixed;
  opacity: 0;
  user-select: auto;  /* to always allow select-all */
}
.extent::after {
  content: '\200b';   /* zero-width space */
}
Enter fullscreen mode Exit fullscreen mode

We then add these at the top and bottom of your page (this could also be done programatically in JS):

<body>
<div class="extent"></div>

<!-- your regular page here -->
<p><a href="https://dev.to/samthor">@samthor</a></p>
<textarea id="input"></textarea>

<div class="extent"></div>
</body>
Enter fullscreen mode Exit fullscreen mode

2. JavaScript listener

By listening to the selectionchange event, we can detect whether both elements are selected in the same gesture. Since our extent elements are transparent, fixed, and have no width, a user can't select them by dragging over them.

The code looks like this:

document.addEventListener('selectionchange', (ev) => {
  const isExtent = (node) => {
    return node instanceof Element && node.classList.contains('extent');
  };

  // check the selection extends over two extent nodes (top and bottom)
  const s = window.getSelection();
  if (s.anchorNode === s.focusNode || !isExtent(s.anchorNode) || !isExtent(s.focusNode)) {
    return;
  }

  // clear page's selection (this isn't perfect and a user may see
  // a flash of selection anyway- use selectstart + rAF to fix this)
  s.removeAllRanges();
  // TODO: focus code goes here
});
Enter fullscreen mode Exit fullscreen mode

3. Focus πŸ”

Finally, you can focus on something! After the call to s.removeAllRanges() above, run something like:

  const main = document.querySelector('textarea#main');  // or whatever you want to focus
  main.focus();
  main.setSelectionRange(0, main.value.length);
Enter fullscreen mode Exit fullscreen mode

This programatically selects the entire contents of textarea#main. You might alternatively want to do something else creative with the Select All gesture! Who knows! πŸ€”

Thanks!

Here's a CodePen that puts the code together for a demo. πŸ‘

A related concept is user-select: none, which disables selection on certain parts of your page. While a complex web app with app-like flows could use it, it's not a solution to the "Select All" problem.

This is just one of many gestures that we can leverage: e.g., this is a follow-up to a post called Native Undo & Redo for the Web, which covers how to insert custom events in the undo stack. And, if you'd like to detect opening "Find" in a page, watch this spaceβ€”the openfind event is coming soon.

4 πŸ‘‹

Top comments (3)

Collapse
 
philnash profile image
Phil Nash

This is cool! Could be something to propose to the dev.to codebase (since you specifically call out having a large editor πŸ˜‰).

Collapse
 
nuxodin profile image
Tobias Buschor

Unfortunately it does not work in Firefox. :(

Collapse
 
samthor profile image
Sam Thorogood Author

I've not had time to test this but I've had another report which was very excited to find out that it does:

Try Emojityperβ€”type some text/emoji, tab-focus to a button, and then try Select All.

Need a better mental model for async/await?

Check out this classic DEV post on the subject.

β­οΈπŸŽ€ JavaScript Visualized: Promises & Async/Await

async await