JSFiddle - React, Tailwind, and code Playground

by alexb

HTML

<div class="content">
<span>Note</span>
     <span>Front-end:</span> <span>Use</span> <span>Angular</span>, <span>React</span> <span>or</span> <span>the</span> <span>framework</span> <span>of</span> <span>your</span> <span>choice</span>.
     <span>Back-end:</span>  <span>Use</span> <span>NodeJS</span> <span>(preferably)</span> <span>+</span> <span>any</span> <span>fancy</span> <span>plugins</span> <span>you</span> <span>find</span> <span>suitable</span> <span>for</span> <span>the</span> <span>task</span> <span>at</span> <span>hand</span>. <span>Other</span> <span>back-ends</span> <span>are</span> <span>acceptable</span> <span>too</span>.

     <span>You</span> <span>can</span> <span>use</span> <span>Google</span>, <span>your</span> <span>Github</span> <span>profile</span>, <span>peek</span> <span>into</span> <span>your</span> <span>older</span> <span>projects</span> <span>or</span> <span>use</span> <span>any</span> <span>code</span> <span>sharing</span> <span>websites</span> <span>you</span> <span>find</span> <span>relevant</span>.

     <span>You</span> <span>can</span> <span>use</span> <span>Wikipedia</span> <span>for</span> <span>the</span> <span>text</span>. <span>~400</span> <span>words</span>. <span>The</span> <span>content</span> <span>and</span> <span>language</span> <span>is</span> <span>irrelevant</span>.

<span>Assignment</span> <span>description</span>
     <span>Develop</span> <span>a</span> <span>Backend</span> <span>+</span> <span>Front-end</span> <span>solution</span> <span>that</span> <span>allows</span> <span>your</span> <span>user</span> <span>to</span> <span>annotate</span> <span>a</span> <span>blob</span> <span>of</span> <span>text</span>.   <span>The</span> <span>user</span> <span>wants</span> <span>to</span> <span>be</span> <span>able</span> <span>to</span> <span>select</span> <span>a</span> <span>substring</span> <span>of</span> <span>the</span> <span>text</span> <span>document</span> <span>and</span> <span>persist</span> <span>the</span>...

CSS

.content {
  white-space: pre-wrap;
}

.content span:hover {
  /* background: cornflowerblue;
  color: white;*/
  cursor: pointer; 
  outline: 1px dashed cornflowerblue;
}

.content span.selected {
  background: cornflowerblue;
  color: white;
}

JavaScript

const selectedTokens = new Set();

function getTokenIndex(el) {
  let index = 0;
	while (el = el.previousSibling) {
  	if (el.tagName === 'SPAN') {
  		index++;
    }
  }
  return index;
}

function toggleToken(e) {
	if (e.target.tagName !== 'SPAN') {
  	return;
  }
  const index = getTokenIndex(e.target);
  if (selectedTokens.has(index)) {
  	selectedTokens.delete(index);
  	e.target.classList.remove('selected');
  }
  else {
  	selectedTokens.add(index);
  	e.target.classList.add('selected');
  }
}

document.querySelector('.content').addEventListener('click', toggleToken);