skip to Main Content

I am trying to figure how to index every word in a text box and maintain these indexes when the text is copy and pasted (into another text area). The problem I am facing is giving each word a unique index even if the word is duplicated.

Currently javascript is duplicating indexes for duplicate words. These words actually each have a unique timecode saved in a database which I later on want to retrieve but can’t do this if there are duplicate indexes.

The aim is to allow users to copy and paste portions of text and then construct a list on indexes from their selections. How can I make sure each words gets a unique index?

// Original string
const originalString = "As you can see the script does not give unique indexes to words that are duplicated in the script. The gets the same index throughout the script.";
document.getElementById('original-text').innerText = originalString;

// Create a map of words to their indexes
const wordIndexMap = {};
originalString.split(/s+/).forEach((word, index) => {
  wordIndexMap[word] = index;
});

// Textbox and output elements
const textBox = document.getElementById('text-box');
const output = document.getElementById('output');

// Event listener for tracking changes in the text box
textBox.addEventListener('input', () => {
  const pastedWords = textBox.value.split(/s+/);
  const indexes = pastedWords
    .filter(word => word in wordIndexMap) // Only track words that are in the original text
    .map(word => wordIndexMap[word]); // Map to their indexes

  // Display the indexes
  output.innerText = `Indexes of words: ${indexes.join(', ')}`;
});
#output {
  margin-top: 10px;
  padding: 10px;
  border: 1px solid #ccc;
  background: #f9f9f9;
}
<h1>Word Index Tracker</h1>
<p>Original Text: <span id="original-text"></span></p>
<textarea id="text-box" rows="5" cols="50" placeholder="Paste text here to see indexes"></textarea>
<div id="output">Indexes will appear here...</div>

2

Answers


  1. // Original string
    const originalString = "As you can see the script does not give unique indexes to words that are duplicated in the script. The gets the same index throughout the script.";
    document.getElementById('original-text').innerText = originalString;
    
    function e( originalString ) {
        let words = originalString.split(/s+/);
        let wordIndexMap = words[ 0 ];
    
        for( let i = 1; i < words.length; i++ ) {
    
             // this line makes the difference, here we make sure that there are 
             // no repeated words.
           if( ! wordIndexMap.includes( words[ i ] )) wordIndexMap += " " + words[ i ];
        }
    
        words = wordIndexMap.split(/s+/);
        let out = [ words.length ];
        for( let i = 1; i < words.length; i++ ) {
           out[ i ] = i;
        }
        return out;
    }
    
    const textBox = document.getElementById('text-box');
    const output = document.getElementById('output');
    
    textBox.addEventListener('input', () => {
      // Display the indexes
      output.innerText = `Indexes of words: ${e(textBox.value) .join(', ')}`;
    });
    #output {
      margin-top: 10px;
      padding: 10px;
      border: 1px solid #ccc;
      background: #f9f9f9;
    }
    <h1>Word Index Tracker</h1>
    <p>Original Text: <span id="original-text"></span></p>
    <textarea id="text-box" rows="5" cols="50" placeholder="Paste text here to see indexes"></textarea>
    <div id="output">Indexes will appear here...</div>
    Login or Signup to reply.
  2. I changed how the user interacts with the page because copy and paste seems awkward and messy. Clicking a button and then clicking the words is simpler and more intuitive.

    Do the following in the example below:

    1. View in Full page mode.
    2. Click the Edit Mode OFF <button>.
      • <button> should turn blue and say: Edit Mode ON.
    3. Click any amount of words in the <fieldset contenteditable>.
      • Each word clicked will be highlighted.
      • Click a highlighted word and the background color is removed.
      • Each highlighted word will be listed by text and index number in the <output> <ol> below.

    Details are commented in the example.

    // 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;
    // Reference <fieldset>
    const src = io.source;
    // Reference <output>
    const sel = io.selected;
    // Declaration for two arrays (not made yet).
    let marked;
    let indices;
    
    /**
     * 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 
     * corresponding index number.
     */
    const indexText = () => {
      const text = src.innerText;
      let marks = text.split(" ");
      marks[0] = `<mark>${marks[0]}`;
      marks[marks.length - 1] = `${marks[marks.length - 1]}</mark>`;
      src.innerHTML = marks.join(`</mark> <mark>`).trim();
      marked = [...document.querySelectorAll("mark")];
      marked.forEach((mrk, idx) => mrk.dataset.idx = idx);
    };
    
    /**
     * 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
     * a word. Each word is inside a <mark> and
     * it's background color will change, and
     * it's text and index number will be listed
     * in output#selected > ol.
     * @param {object} e - Event object
     */
    const highLight = (e) => {
      const clk = e.target;
      if (clk.matches("mark")) {
        clk.classList.toggle("highlight");
      }
      indices = [...document.querySelectorAll(".highlight")]
        .map(h => {
          let txt = h.innerText;
          txt = txt.replace(/[^A-Z0-9]$/i, "");
          return `
        <li>${txt}
        ${h.dataset.idx}</li>
        `;
        });
      sel.firstElementChild.innerHTML = indices.join("");
    };
    
    btn.addEventListener("click", editText);
    
    src.addEventListener("click", highLight);
    :root {
      font: 2ch/1.5 "Segoe UI"
    }
    
    #toggle {
      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%);
    }
    
    #toggle.off::after {
      content: " OFF";
    }
    
    .off + #source {
      opacity: 0.7;
      pointer-events: none;
    }
    
    #source {
      min-height: 5rem;
      padding-top: 0.75rem;
      border-radius: 4px;
    }
    
    mark {
      background-color: transparent
    }
    
    mark.highlight {
      background: gold;
    }
    <form id="indexer">
      <button id="toggle" class="off" type="button"></button>
      <fieldset id="source">
        Well, the way they make shows is, they make one show. That show's called a pilot. Then they show that show to the people who make shows, and on the strength of that one show they decide if they're going to make more shows. Some pilots get picked and become television programs. Some don't, become nothing. She starred in one of the ones that became nothing.
      </fieldset>
      <output id="selected">
        <ol></ol>
      </output>
    </form>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search