Javascript 突出显示光标前后的文本

问题描述 投票:0回答:1

我有一个任务,用户需要用光标突出显示部分文本。但是,我现在处理鼠标移动事件的方式仅允许用户在单个方向上突出显示文本 - 即,如果他们在移动事件中更改鼠标方向,则文本突出显示不会跟随他们的光标。

这个post与请求相关,但通过添加/切换类来处理文本突出显示,而我通过更改跨度元素的背景颜色来处理突出显示。这个post也非常相关,但我不知道如何实施。

我希望弄清楚如何在鼠标改变方向时删除突出显示。这可能吗?

// Initialize data structures and variables
        var wordDictionary = {};         // Dictionary to store words
        var selectedWords = {};          // Currently selected words
        var isMouseDown = false;         // Flag for mouse state
        var currentColor = '';           // Current highlight color
        var usedColors = new Set();      // Set of used colors
        // Available highlight colors
        var availableColors = ["yellow", "red", "blue", "green", "orange"]; 
        var highlights = {};             // Store highlighted words for each event
        var eventCounter = 0;            // Counter for events

        var text = "Pangolins, sometimes known as scaly anteaters, are mammals of the order Pholidota. \
      The one extant family, the Manidae, has three genera: Manis, Phataginus, and Smutsia. \
       Manis comprises four species found in Asia, while Phataginus and Smutsia include two species each, all found in sub-Saharan Africa. \
        These species range in size from 30 to 100 cm (12 to 39 in). \
        A number of extinct pangolin species are also known. \
        In September 2023, nine species were reported.<br><br> \
        Pangolins have large, protective keratin scales, similar in material to fingernails and toenails, covering their skin; \
        they are the only known mammals with this feature. \
        They live in hollow trees or burrows, depending on the species. \
        Pangolins are nocturnal, and their diet consists of mainly ants and termites, which they capture using their long tongues. \
        They tend to be solitary animals, meeting only to mate and produce a litter of one to three offspring, which they raise for about two years.";

        var textParagraph = document.getElementById("textParagraph");
        textParagraph.innerHTML = text;

        // Execute when the window is loaded
        window.onload = function () {
            var contentDiv = document.getElementById('content');
            let ptag = contentDiv.querySelector('p');
            var text = ptag.innerHTML.trim();
            var words = text.split(/\s+|(?=<br><br>)/);

            // Populate wordDictionary with words and create span elements for each word
            for (var i = 0; i < words.length; i++) {
                wordDictionary[i] = words[i];
            }

            for (var i = 0; i < words.length; i++) {
                var wordElement = document.createElement('span');
                wordElement.textContent = words[i] + ' ';
                wordElement.dataset.index = i;
                wordElement.addEventListener('mousedown', handleMouseDown);
                if (words[i] == '<br><br>') {
                    wordElement.textContent = ' ';
                    wordElement.classList.add('line-break')
                }
                contentDiv.appendChild(wordElement);
            }

            // Add mouseup event listener for handling mouse up events
            document.addEventListener('mouseup', handleMouseUp);
        };

        // Function to get a random highlight color
        function getRandomColor() {
            if (availableColors.length === 0) {
                availableColors = [...usedColors];
                usedColors.clear();
            }

            var randomIndex = Math.floor(Math.random() * availableColors.length);
            var color = availableColors.splice(randomIndex, 1)[0];
            usedColors.add(color);
            return color;
        }

        // Function to handle mouse down event on words
        function handleMouseDown(event) {
            isMouseDown = true;
            var index = event.target.dataset.index;
            var word = event.target.textContent.trim();

            if (!isHighlighted(index)) {
                selectedWords = {};
                selectedWords.startIndex = index;
                selectedWords.endIndex = index;

                selectedWords[index] = word;
                // console.log(selectedWords)
                currentColor = getRandomColor();
                event.target.style.backgroundColor = currentColor;

                event.preventDefault();

                document.addEventListener('mousemove', handleMouseMove);
            }
        }

        // Function to handle mouse up event
        function handleMouseUp(event) {
            if (isMouseDown) {
                document.removeEventListener('mousemove', handleMouseMove);
                var highlightedWords = {};

                for (var index in selectedWords) {
                    if (index !== 'startIndex' && index !== 'endIndex') {
                        highlightedWords[index] = selectedWords[index];
                    }
                }

                eventCounter++;
                highlights[eventCounter] = highlightedWords;
                // console.log(highlights);
            }

            isMouseDown = false;
        }

        // Function to handle mouse move event (word selection)
        function handleMouseMove(event) {
            if (isMouseDown) {
                var currentIndex = event.target.dataset.index;
                var startIndex = selectedWords.startIndex;
                var endIndex = selectedWords.endIndex;
                var contentDiv = document.getElementById('content');

                var newStartIndex = Math.min(startIndex, currentIndex);
                var newEndIndex = Math.max(endIndex, currentIndex);

                clearPreviousSelection();

                for (var i = newStartIndex; i <= newEndIndex; i++) {
                    selectedWords[i] = wordDictionary[i];
                }

                for (var i = newStartIndex + 1; i <= newEndIndex + 1; i++) {
                    contentDiv.children[i].style.backgroundColor = currentColor;
                }

                selectedWords.startIndex = newStartIndex;
                selectedWords.endIndex = newEndIndex;
            }
        }

        // Function to clear previously selected words
        function clearPreviousSelection() {
            var contentDiv = document.getElementById('content');

            for (var i in selectedWords) {
                if (i !== 'startIndex' && i !== 'endIndex') {
                    contentDiv.children[i].style.backgroundColor = '';
                    delete selectedWords[i];
                }
            }
        }

        // Function to check if a word is already highlighted
        function isHighlighted(index) {
            for (var eventKey in highlights) {
                var highlightedWords = highlights[eventKey];

                for (var wordIndex in highlightedWords) {
                    if (wordIndex === index) {
                        return true;
                    }
                }
            }

            return false;
        }

        // Function to clear all selections and reset
        function clearSelections() {
            var contentDiv = document.getElementById('content');
            var wordElements = contentDiv.getElementsByTagName('span');

            for (var i = 0; i < wordElements.length; i++) {
                wordElements[i].style.backgroundColor = '';
            }

            highlights = {};
            eventCounter = 0;
        }

        // Function to undo the last selection
        function undoSelection() {
            if (eventCounter > 0) {
                var lastHighlight = highlights[eventCounter];

                for (var index in lastHighlight) {
                    var wordIndex = parseInt(index);
                    var contentDiv = document.getElementById('content');

                    if (!isNaN(wordIndex)) {
                        contentDiv.children[wordIndex + 1].style.backgroundColor = '';
                    }
                }

                delete highlights[eventCounter];
                eventCounter--;
            }
        }

        // Add event listeners to the clear and undo buttons
        document.getElementById("removeHighlight").addEventListener("click", clearSelections);
        document.getElementById("undoHighlight").addEventListener("click", undoSelection);
        #buttons {
            margin-top: 30px;
        }

        .line-break {
            display: block;
            margin: 15px;
        }

        #trial_display {
            display: block;
            padding: 50px;
        }

        #title{
            text-align: center;
            font-size: 20px;
        }

        #content {
            display: block;
            border: 2px solid gray;
            padding: 50px;
        }
    <div id="trial_display">
    <div id="content">
        <p id="textParagraph" style="display: none"></p> 
      </div>

    <div id="buttons">
        <button id="removeHighlight">Clear</button>
        <button id="undoHighlight">Undo</button>
    </div>
</div>

javascript html jquery css mouseevent
1个回答
0
投票

看来你想要的,已经通过浏览器的选择API本地实现了。

因此,您可以通过选择来处理这一切,通过从中获取元素,然后通过其数据索引更改默认选择突出显示样式。

(注意:这里我没有实现undo和clear,因为它需要大量重构,但这应该不难,我也保留了整个原始代码,除了移动的window.onload和禁用的事件侦听器,所以现在应该完全重构)

试试这个:

  1. 从选择中提取元素

  2. 循环每个元素,获取索引,然后更改其背景

  3. 每次选择开始时,删除之前的选择规则,并添加新的颜色

  1. 从选择中提取元素

     function getSelectionElements(e) {
    
         const sel = window.getSelection();
    
         if (sel.rangeCount) {
    
             // container to store all seleccted elements
             const container = document.createElement('div');
    
             for (let i = 0, len = sel.rangeCount; i < len; ++i) {
                 // append elements
                 // if only single text node, then append element from event.target
                 if (sel.getRangeAt(i).cloneContents().childNodes.length > 1) {
                     container.appendChild(sel.getRangeAt(i).cloneContents());
                 } else {
                     container.appendChild(e.target.cloneNode());
                 }
             }
    
             return container;
         }
    
     }
    
  1. 循环每个元素,获取索引,然后更改其背景

     // get elements from selection
     // loop and add background color to each
     document.addEventListener('mouseup', (e) => {
    
    
         const selectedElements = getSelectionElements(e);
    
    
         if (selectedElements)
    
             selectedElements.childNodes.forEach(el => {
    
             let index = el.dataset.index;
             let word = el.textContent.trim();
    
    
             if (!isHighlighted(index)) {
                 selectedWords = {};
                 selectedWords.startIndex = index;
                 selectedWords.endIndex = index;
    
                 selectedWords[index] = word;
    
                 document.querySelector(`[data-index="${index}"]`).style.backgroundColor = currentColor;
    
             }
         });
    
    
     });
    
  1. 每次选择开始时,删除之前的选择规则,并添加新的颜色

     // remove previous selection style
     // add new highlight color
     document.addEventListener('mousedown', (e) => {
    
         const sheet = window.document.styleSheets[0];
    
    
         const rules = document.styleSheets[0].cssRules;
    
         for (let i = 0; i < rules.length; i++) {
             const rule = rules[i];
    
             if (rule.selectorText.includes('::selection')) sheet.deleteRule(i);
         }
    
         currentColor = getRandomColor();
    
    
         sheet.insertRule(`#content span::selection { background-color: ${currentColor}; }`, sheet.cssRules.length);
    
     });
    

// Execute when the window is loaded
window.onload = function() {

  // Initialize data structures and variables
  var wordDictionary = {}; // Dictionary to store words
  var selectedWords = {}; // Currently selected words
  var isMouseDown = false; // Flag for mouse state
  var currentColor = ''; // Current highlight color
  var usedColors = new Set(); // Set of used colors
  // Available highlight colors
  var availableColors = ["yellow", "red", "blue", "green", "orange"];
  var highlights = {}; // Store highlighted words for each event
  var eventCounter = 0; // Counter for events

  var text = "Pangolins, sometimes known as scaly anteaters, are mammals of the order Pholidota. \
      The one extant family, the Manidae, has three genera: Manis, Phataginus, and Smutsia. \
       Manis comprises four species found in Asia, while Phataginus and Smutsia include two species each, all found in sub-Saharan Africa. \
        These species range in size from 30 to 100 cm (12 to 39 in). \
        A number of extinct pangolin species are also known. \
        In September 2023, nine species were reported.<br><br> \
        Pangolins have large, protective keratin scales, similar in material to fingernails and toenails, covering their skin; \
        they are the only known mammals with this feature. \
        They live in hollow trees or burrows, depending on the species. \
        Pangolins are nocturnal, and their diet consists of mainly ants and termites, which they capture using their long tongues. \
        They tend to be solitary animals, meeting only to mate and produce a litter of one to three offspring, which they raise for about two years.";

  var textParagraph = document.getElementById("textParagraph");
  textParagraph.innerHTML = text;


  var contentDiv = document.getElementById('content');
  let ptag = contentDiv.querySelector('p');
  var text = ptag.innerHTML.trim();
  var words = text.split(/\s+|(?=<br><br>)/);

  // Populate wordDictionary with words and create span elements for each word
  for (var i = 0; i < words.length; i++) {
    wordDictionary[i] = words[i];
  }

  for (var i = 0; i < words.length; i++) {
    var wordElement = document.createElement('span');
    wordElement.textContent = words[i] + ' ';
    wordElement.dataset.index = i;
    //wordElement.addEventListener('mousedown', handleMouseDown);
    if (words[i] == '<br><br>') {
      wordElement.textContent = ' ';
      wordElement.classList.add('line-break')
    }
    contentDiv.appendChild(wordElement);
  }

  // Add mouseup event listener for handling mouse up events
  //document.addEventListener('mouseup', handleMouseUp);


  // Function to get a random highlight color
  function getRandomColor() {
    if (availableColors.length === 0) {
      availableColors = [...usedColors];
      usedColors.clear();
    }

    var randomIndex = Math.floor(Math.random() * availableColors.length);
    var color = availableColors.splice(randomIndex, 1)[0];
    usedColors.add(color);
    return color;
  }

  // Function to handle mouse down event on words
  function handleMouseDown(event) {

    isMouseDown = true;
    var index = event.target.dataset.index;
    var word = event.target.textContent.trim();

    if (!isHighlighted(index)) {
      selectedWords = {};
      selectedWords.startIndex = index;
      selectedWords.endIndex = index;

      selectedWords[index] = word;
      // console.log(selectedWords)
      currentColor = getRandomColor();
      event.target.style.backgroundColor = currentColor;

      event.preventDefault();

      //document.addEventListener('mousemove', handleMouseMove);
    }
  }

  // Function to handle mouse up event
  function handleMouseUp(event) {
    if (isMouseDown) {
      document.removeEventListener('mousemove', handleMouseMove);
      var highlightedWords = {};

      for (var index in selectedWords) {
        if (index !== 'startIndex' && index !== 'endIndex') {
          highlightedWords[index] = selectedWords[index];
        }
      }

      eventCounter++;
      highlights[eventCounter] = highlightedWords;
      // console.log(highlights);
    }

    isMouseDown = false;
  }

  // Function to handle mouse move event (word selection)
  function handleMouseMove(event) {
    if (isMouseDown) {
      var currentIndex = event.target.dataset.index;
      var startIndex = selectedWords.startIndex;
      var endIndex = selectedWords.endIndex;
      var contentDiv = document.getElementById('content');

      var newStartIndex = Math.min(startIndex, currentIndex);
      var newEndIndex = Math.max(endIndex, currentIndex);

      clearPreviousSelection();

      for (var i = newStartIndex; i <= newEndIndex; i++) {
        selectedWords[i] = wordDictionary[i];
      }

      for (var i = newStartIndex + 1; i <= newEndIndex + 1; i++) {
        contentDiv.children[i].style.backgroundColor = currentColor;
      }

      selectedWords.startIndex = newStartIndex;
      selectedWords.endIndex = newEndIndex;
    }
  }

  // Function to clear previously selected words
  function clearPreviousSelection() {
    var contentDiv = document.getElementById('content');

    for (var i in selectedWords) {
      if (i !== 'startIndex' && i !== 'endIndex') {
        contentDiv.children[i].style.backgroundColor = '';
        delete selectedWords[i];
      }
    }
  }

  // Function to check if a word is already highlighted
  function isHighlighted(index) {
    for (var eventKey in highlights) {
      var highlightedWords = highlights[eventKey];

      for (var wordIndex in highlightedWords) {
        if (wordIndex === index) {
          return true;
        }
      }
    }

    return false;
  }

  // Function to clear all selections and reset
  function clearSelections() {
    var contentDiv = document.getElementById('content');
    var wordElements = contentDiv.getElementsByTagName('span');

    for (var i = 0; i < wordElements.length; i++) {
      wordElements[i].style.backgroundColor = '';
    }

    highlights = {};
    eventCounter = 0;
  }

  // Function to undo the last selection
  function undoSelection() {
    if (eventCounter > 0) {
      var lastHighlight = highlights[eventCounter];

      for (var index in lastHighlight) {
        var wordIndex = parseInt(index);
        var contentDiv = document.getElementById('content');

        if (!isNaN(wordIndex)) {
          contentDiv.children[wordIndex + 1].style.backgroundColor = '';
        }
      }

      delete highlights[eventCounter];
      eventCounter--;
    }
  }

  // Add event listeners to the clear and undo buttons
  document.getElementById("removeHighlight").addEventListener("click", clearSelections);
  document.getElementById("undoHighlight").addEventListener("click", undoSelection);



  function getSelectionElements(e) {

    const sel = window.getSelection();

    if (sel.rangeCount) {

      // container to store all seleccted elements
      const container = document.createElement('div');

      for (let i = 0, len = sel.rangeCount; i < len; ++i) {
        // append elements
        // if only single text node, then append element from event.target
        if (sel.getRangeAt(i).cloneContents().childNodes.length > 1) {
          container.appendChild(sel.getRangeAt(i).cloneContents());
        } else {
          container.appendChild(e.target.cloneNode());
        }
      }

      return container;
    }


  }

  // remove previous selection style
  // add new highlight color
  document.addEventListener('mousedown', (e) => {

    const sheet = window.document.styleSheets[0];


    const rules = document.styleSheets[0].cssRules;

    for (let i = 0; i < rules.length; i++) {
      const rule = rules[i];

      if (rule.selectorText.includes('::selection')) sheet.deleteRule(i);
    }

    currentColor = getRandomColor();


    sheet.insertRule(`#content span::selection { background-color: ${currentColor}; }`, sheet.cssRules.length);

  });


  // get elements from selection
  // loop and add background color to each
  document.addEventListener('mouseup', (e) => {


    const selectedElements = getSelectionElements(e);


    if (selectedElements)

      selectedElements.childNodes.forEach(el => {

        let index = el.dataset.index;
        let word = el.textContent.trim();


        if (!isHighlighted(index)) {
          selectedWords = {};
          selectedWords.startIndex = index;
          selectedWords.endIndex = index;

          selectedWords[index] = word;

          document.querySelector(`[data-index="${index}"]`).style.backgroundColor = currentColor;

        }
      });


  });


};
#buttons {
  margin-top: 30px;
}

.line-break {
  display: block;
  margin: 15px;
}

#trial_display {
  display: block;
  padding: 50px;
}

#title {
  text-align: center;
  font-size: 20px;
}

#content {
  display: block;
  border: 2px solid gray;
  padding: 50px;
}
<div id="trial_display">
  <div id="content">
    <p id="textParagraph" style="display:none"></p>
  </div>
  <div id="buttons">
    <button id="removeHighlight">Clear</button>
    <button id="undoHighlight">Undo</button>
  </div>
</div>

© www.soinside.com 2019 - 2024. All rights reserved.