Skip to content

Instantly share code, notes, and snippets.

@bvaughn
Last active February 28, 2024 20:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bvaughn/6499f35093fe7d75265f73d736228bde to your computer and use it in GitHub Desktop.
Save bvaughn/6499f35093fe7d75265f73d736228bde to your computer and use it in GitHub Desktop.
Chrome's copy+paste behavior and content editables

Start with this HTML:

<div>click here</div>
<div style="user-select: none;">read-only (no-select)</div>
<div contenteditable style="user-select: none;">editable (no-select)</div>
<div>release here</div>

Click at the top ("click here") and drag down and release at the bottom ("release here"). The following text will be selected:

Screenshot 2024-02-28 at 3 16 26 PM

If you were to copy+paste, you would see the same:

click here
editable (no-select)
release here

Arguably the "editable (no-select)" text should not be included, since it is styled with user-select: none;, but it is selected anyway. (FWIW I don't think this is crazy, since editing text often requires selecting it.)

Still, what if you wanted to preserve the text editable behavior while also excluding it from selection? You could try disabling it conditionally:

<div
  id="target"
  contenteditable="false"
  style="user-select: none;"
  tabindex="0"
/>

<script>
  const target = document.getElementById('target');
  // "focusin" behaves the same as "focus"
  target.addEventListener('focus', () => {
    target.setAttribute('contenteditable', true);
  });
  target.addEventListener('mousedown', () => {
    target.setAttribute('contenteditable', true);
  });
  target.addEventListener('blur', () => {
    target.setAttribute('contenteditable', false);
  });
</script>

This approach almost works!

  • ✅ Clicking into the item works; you can edit
  • ✅ Tabbing (or clicking) out of it works; selection behavior is correct
  • ❌ Tabbing into the item doesn't work; it receives focus and the attribute updates, but the text remains non editable

So how would you approach this? Any suggestions?

@bvaughn
Copy link
Author

bvaughn commented Feb 28, 2024

I believe the following might work.

<div id="wrapper" tabindex="0">
  <div id="target" contenteditable="false" style="user-select: none;" />
</div>

<script>
  const target = document.getElementById('target');
  const wrapper = document.getElementById('wrapper');

  wrapper.addEventListener('focus', () => {
    target.setAttribute('contenteditable', true);
    target.focus();
  });
  target.addEventListener('mousedown', () => {
    target.setAttribute('contenteditable', true);
  });
  target.addEventListener('blur', () => {
    target.setAttribute('contenteditable', false);
  });
</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment