Detecting Select All on the Web
Tags: javascript, gesture
Published: 2019-06-04 00:36:28

---
title: Detecting Select All on the Web
published: true
description: Let's intercept the Select All gesture and do something better! 
tags: javascript, gesture
series: Blog-A-Day in June
cover_image: https://thepracticaldev.s3.amazonaws.com/i/xciipbfxt5fpfs84iv7a.png
---

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](https://thepracticaldev.s3.amazonaws.com/i/8zrap2bml7178v4yrh2i.gif)

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](https://emojityper.com). 😂⌨️

If you've read [my other posts](https://dev.to/chromiumdev/-native-undo--redo-for-the-web-3fl3), 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](https://developer.mozilla.org/en-US/docs/Web/API/Document/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:

```css
.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):

```html
<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:

```js
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:

```js
  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](https://codepen.io/samthor/pen/mYorrO) that puts the code together for a demo. 👍

A related concept is [`user-select: none`](https://dev.to/lynnewritescode/5-css-tips-you-didnt-know-you-needed-nb3#prevent-highlighting), 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 &amp; Redo for the Web](https://dev.to/chromiumdev/-native-undo--redo-for-the-web-3fl3), 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](https://www.chromestatus.com/feature/6636647374389248) event is coming soon.

4 👋