Save selection text and show it later in html and javascript

后端 未结 8 900
悲哀的现实
悲哀的现实 2020-12-14 18:39

I have a difficult situation with html and javascript. My html page allows user to select text and highlight it with colors. Now I want to save the state into database to sh

8条回答
  •  南方客
    南方客 (楼主)
    2020-12-14 19:08

    Range objects and document.execCommand allow to manipulate selection pretty easily. The main problem in your case is saving the range object in a text format.

    Basically what you need is to get the startContainer, startOffset, endContainer and endOffset, which are the values needed to create Range objects. Offsets are number so it's pretty straightforward. Containers are Nodes, which you can't directly save as strings, so that's the main problem. One thing you can do is add keys to your DOM and save the key. But then, since in ranges containers are text nodes, you'll need to save the index of the text node. Something like this should allow to tag the DOM with keys, using a recursive function:

    function addKey(element) {
      if (element.children.length > 0) {
        Array.prototype.forEach.call(element.children, function(each, i) {
          each.dataset.key = key++;
          addKey(each)
        });
      }
    };
    
    addKey(document.body);
    

    Once this is done, you can convert range objects to an object that you can save as a string. Like this:

    function rangeToObj(range) {
      return {
        startKey: range.startContainer.parentNode.dataset.key,
        startTextIndex: Array.prototype.indexOf.call(range.startContainer.parentNode.childNodes, range.startContainer),
        endKey: range.endContainer.parentNode.dataset.key,
        endTextIndex: Array.prototype.indexOf.call(range.endContainer.parentNode.childNodes, range.endContainer),
        startOffset: range.startOffset,
        endOffset: range.endOffset
      }
    }
    

    Using this, you can save each selection that the user creates to an array. Like this:

    document.getElementById('textToSelect').addEventListener('mouseup', function(e) {
      if (confirm('highlight?')) {
        var range = document.getSelection().getRangeAt(0);
        selectArray.push(rangeToObj(range));
        document.execCommand('hiliteColor', false, 'yellow')
      }
    });
    

    To save the highlights, you save each object to JSON. To test this, you can just get the JSON string from your range objects array. Like this (this is using the get Seletion button at the top):

    document.getElementById('getSelectionString').addEventListener('click', function() {
      alert('Copy string to save selections: ' + JSON.stringify(selectArray));
    });
    

    Then when loading the empty HTML, you can use a reverse function that will create ranges from the objects you saved in JSON. Like this:

    function objToRange(rangeStr) {
      range = document.createRange();
      range.setStart(document.querySelector('[data-key="' + rangeStr.startKey + '"]').childNodes[rangeStr.startTextIndex], rangeStr.startOffset);
      range.setEnd(document.querySelector('[data-key="' + rangeStr.endKey + '"]').childNodes[rangeStr.endTextIndex], rangeStr.endOffset);
      return range;
    }
    

    So you could have an array of ranges in strings that you convert to objects, and then convert to Range objects that you can add. Then using execCommand, you set some formatting. Like this (this is using the set selection button at the top, you do this after refreshing the fiddle):

    document.getElementById('setSelection').addEventListener('click', function() {
      var selStr = prompt('Paste string');
      var selArr = JSON.parse(selStr);
      var sel = getSelection();
      selArr.forEach(function(each) {
        sel.removeAllRanges();
        sel.addRange(objToRange(each));
        document.execCommand('hiliteColor', false, 'yellow')
      })
    });
    

    See: https://jsfiddle.net/sek4tr2f/3/

    Note that there are cases where this won't work, main problematic case is when user selects content in already highlighted content. These cases can be handled, but you'll need more conditions.

提交回复
热议问题