loading...
Cover image for Detecting Select All on the Web
Google Web Dev

Detecting Select All on the Web

samthor profile image Sam Thorogood ・3 min read

Blog-A-Day in June (19 Part Series)

1) Rebuild only when necessary in Node 2) Civilization is a game you never lose 3 ... 17 3) Arrow functions break JavaScript parsers 4) Detecting Select All on the Web 5) Declaring JS Variables in 2019 6) Sam's dotfiles highlights 7) Automate Reading Form Results with 🤖 Chrome 8) Beyond appendChild: Better convenience methods for HTML 9) AMA, Sam 10-yr Googler in Web DevRel 10) Disable a HTML form while in-flight using fieldset 11) PWAs that download like apps 🗜️ 12) Matching elements with selectors in JS 13) Install This PWA To Continue 14) Google Assistant now supports "Open/Close" devices 15) Modern Web Components 16) What To Expect When You're Expecting To Drop IE11 🗑️ 17) Divert Vertical Scroll To The Side ↔️ 18) Graceful Shutdown Is A Lie 19) Progress Indicator With Fetch

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 */
}

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>

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
});

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);

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 👋

Blog-A-Day in June (19 Part Series)

1) Rebuild only when necessary in Node 2) Civilization is a game you never lose 3 ... 17 3) Arrow functions break JavaScript parsers 4) Detecting Select All on the Web 5) Declaring JS Variables in 2019 6) Sam's dotfiles highlights 7) Automate Reading Form Results with 🤖 Chrome 8) Beyond appendChild: Better convenience methods for HTML 9) AMA, Sam 10-yr Googler in Web DevRel 10) Disable a HTML form while in-flight using fieldset 11) PWAs that download like apps 🗜️ 12) Matching elements with selectors in JS 13) Install This PWA To Continue 14) Google Assistant now supports "Open/Close" devices 15) Modern Web Components 16) What To Expect When You're Expecting To Drop IE11 🗑️ 17) Divert Vertical Scroll To The Side ↔️ 18) Graceful Shutdown Is A Lie 19) Progress Indicator With Fetch

Posted on by:

samthor profile

Sam Thorogood

@samthor

Developer Relations for Web at Google.

Google Web Dev

Collected thoughts and posts on web development from the @ChromiumDev team.

Discussion

markdown guide
 

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

 

Unfortunately it does not work in Firefox. :(

 

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.