stack_tweaked 4
by Julian
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Word Index Tracker</title>
</head>
<body>
<form id="indexer">
<button id="toggle" class="off" type="button"></button>
<button id="add-selection" type="button">Add Selected Text</button>
<fieldset id="source">
Paste text here
</fieldset>
<output id="selected">
<ol></ol>
</output>
</form>
</body>
</html>
CSS
:root {
font: 2ch/1.5 "Segoe UI"
}
#toggle, #add-selection {
margin-bottom: 1rem;
padding: 5px 10px;
border-radius: 8px;
font: inherit;
cursor: pointer;
background:
radial-gradient(
ellipse at center,
rgba(197,222,234,1) 0%,
rgba(138,187,215,1) 31%,
rgba(6,109,171,1) 100%);
}
#toggle::before {
content: "Edit Mode"
}
#toggle::after {
content: " ON";
}
#toggle.off {
color: #fff;
background:
radial-gradient(
ellipse at center,
rgba(125, 126, 125, 1) 0%,
rgba(14, 14, 14, 1) 100%);
}
#add-selection {
display: inline-block;
}
#source[contenteditable="true"] + #add-selection {
display: inline-block;
}
JavaScript
// Reference <form>
const form = document.forms.indexer;
/**
* Reference all form controls of <form>
* In this layout it is:
* - <button>
* - <fieldset>
* - <output>
*/
const io = form.elements;
// Reference <button>
const btn = io.toggle;
const addSelectionBtn = io['add-selection'];
// Reference <fieldset>
const src = io.source;
// Reference <output>
const sel = io.selected;
/**
* Index each word by extracting the string,
* converting the string into an array of
* substrings, and wrapping each word with
* a <mark> element. Each <mark> is then
* assigned a [data-idx] attribute with the
* index starting from 1.
*/
const indexText = () => {
const text = src.innerText;
let marks = text.split(/\s+/);
marks = marks.map((word, idx) => {
const wordIndex = idx + 1; // Index starts from 1
return `<mark data-idx="${wordIndex}">${word}</mark>`;
});
src.innerHTML = marks.join(" ").trim();
};
/**
* Handle "click" event when the user clicks
* button#toggle. Basically an on/off switch
* for whether fieldset#source can be edited
* and when the text can be indexed.
* @param {object} e - Event object
*/
const editText = (e) => {
e.target.classList.toggle("off");
src.toggleAttribute("contenteditable");
indexText();
};
/**
* Handle "click" event when user clicks "Add Selected Text" button.
* Adds the selected portion of text along with its start and end indices.
*/
const addSelection = () => {
const selection = window.getSelection();
const selectedText = selection.toString().trim();
if (selectedText) {
const words = src.innerText.split(/\s+/);
const startIdx = words.indexOf(selectedText) + 1; // 1-based index
const endIdx = startIdx; // For single word selection
sel.firstElementChild.innerHTML += `<li>${selectedText} (Start Index: ${startIdx}, End Index: ${endIdx})</li>`;
}
};
btn.addEventListener("click", editText);
addSelectionBtn.addEventListener("click", addSelection);