1

The Selection: containsNode() method takes an optional second argument i.e. partialContainment.

When I set this to true, it seems to work ok, but sometimes picks up neighboring elements that are not part of the visible selection. I set it to false to be more strict, but it misses elements.

Is there a difference between the visible "highlight" on the page and how the API interprets what is "selected"?

In the example below, if you select partial text, some of the letters do not become active, even though they are visibly highlighted. What do I need to change to get the text to become red for anything that appears selected?

const sequences = ['CRABAPPLE', 'ORANGES', 'SQUASH'];

const handleTextSelection = (e) => {
  const selection = window.getSelection();
  const $container = $(e.currentTarget);
  const $sequence = $(e.target).closest('.sequence');
  $sequence.find('.letter').each((i, el) => {
    $(el).toggleClass('letter-active', selection.containsNode(el, false));
  });
  selection.empty();
};

$('#container').append(sequences.map(sequence =>
  $('<div>', { class: 'sequence' }).append(sequence.split('').map(letter =>
    $('<div>', { class: 'letter', text: letter })))));
    
$('#container').on('mouseup', handleTextSelection);
html, body, #container { width: 100%; height: 100%; margin: 0; padding: 0; }
#container { display: flex; flex-direction: row; gap: 0.25rem; align-items: center; justify-content: center; }
.sequence { display: flex; flex-direction: row; }
.sequence:after { content: ','; }
.sequence:last-child:after { content: none; }
.letter-active { color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container"><div>

Update

The following seems to work a little better, but it still counts partial (not actually selected) letters as selected.

const sequences = ['CRABAPPLE', 'ORANGES', 'SQUASH'];

const getLetter = (node) => {
  let letter = node;
  if (node.nodeName === '#text') { node = node.parentElement; }
  return node.classList.contains('letter') ? node : null;
};

const handleTextSelection = (e) => {
  const selection = window.getSelection();
  const range = selection.getRangeAt(0);
  const { startContainer, endContainer } = range
  const $container = $(e.currentTarget);
  const $sequence = $(range.commonAncestorContainer);
  const letterStart = getLetter(startContainer);
  const letterEnd = getLetter(endContainer);
  if (!letterStart || !letterEnd) { return; }
  let active = false;
  $sequence.find('.letter').each((i, el) => {
    if (el === letterStart) { active = true; }
    $(el).toggleClass('letter-active', active);
    if (el === letterEnd) { active = false; }
  });
  selection.empty();
};

$('#container').append(sequences.map(sequence =>
  $('<div>', { class: 'sequence' }).append(sequence.split('').map(letter =>
    $('<div>', { class: 'letter', text: letter })))));
    
$('#container').on('mouseup', handleTextSelection);
html, body, #container { width: 100%; height: 100%; margin: 0; padding: 0; }
#container { display: flex; flex-direction: row; gap: 0.25rem; align-items: center; justify-content: center; }
.sequence { display: flex; flex-direction: row; }
.sequence:after { content: ','; }
.sequence:last-child:after { content: none; }
.letter-active { color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container"><div>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132

0 Answers0