保存选择文本,稍后在html和javascript中显示

时间:2013-09-22 17:18:09

标签: javascript jquery html

我遇到了html和javascript的困境。 我的html页面允许用户选择文本并用颜色突出显示。现在我想将状态保存到数据库中以便稍后为该用户显示它。当然,我可以在用户编辑后保存整个html。但我只想保存一些参数,结合原始html显示状态用户上次查看的页面。我们可以使用这个功能:

var index = innerHTML.indexOf(text);

突出显示该索引处的文字。但是如果页面中有很多相同的文本,我想要突出显示之前用户突出显示的文字。

任何人都可以通过javascript告诉我如何实现这一目标?

我非常感谢你的帮助。

8 个答案:

答案 0 :(得分:7)

Range个对象和document.execCommand允许很容易地操纵选择。您的主要问题是以文本格式保存范围对象。

基本上您需要的是获取startContainerstartOffsetendContainerendOffset,它们是创建Range对象所需的值。 Offsets是数字,因此非常简单。容器是节点,您无法直接将其保存为字符串,因此这是主要问题。您可以做的一件事是为DOM添加密钥并保存密钥。但是,由于在范围容器中是文本节点,因此您需要保存文本节点的索引。这样的东西应该允许使用递归函数用键标记DOM:

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);

完成此操作后,您可以将范围对象转换为可以保存为字符串的对象。像这样:

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
  }
}

使用此选项,您可以将用户创建的每个选项保存到数组中。像这样:

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')
  }
});

要保存突出显示,请将每个对象保存为JSON。要对此进行测试,您只需从范围对象数组中获取JSON字符串即可。像这样(这是使用顶部的get Seletion按钮):

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

然后在加载空HTML时,您可以使用反向函数,该函数将使用您在JSON中保存的对象创建范围。像这样:

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;
}

因此,您可以在字符串中使用转换为对象的范围数组,然后转换为可以添加的Range对象。然后使用execCommand设置一些格式。像这样(这是使用顶部的设置选择按钮,你在刷新小提琴后这样做):

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')
  })
});

请参阅:https://jsfiddle.net/sek4tr2f/3/

请注意,有些情况下这不起作用,主要问题是用户选择已突出显示的内容中的内容。可以处理这些情况,但您需要更多条件。

答案 1 :(得分:2)

您需要捕获节点的路径以了解其位置 这可以通过多种方式完成 最简单的方法是将dom遍历到主体并创建一个选择器。

function getPathFromElement(element) {
    var stack = [];

    while (element.parentNode != document.documentElement) {
        var sibCount = 0;
        var sibIndex = 0;
        var childNodes = element.parentNode.childNodes;
        var childLength = childNodes.length;

        for (var i = 0; i < childLength; i++) {
            var sib = childNodes[i];

            if (sib.nodeName == element.nodeName) {
                if (sib === element) {
                    sibIndex = sibCount;
                }

                sibCount++;
            }
        }

        if (element.hasAttribute("id") && element.id !== "") {
            stack.unshift(`${element.nodeName.toLowerCase()}#${element.id}`);
        }
        else if (sibCount > 1) {
            stack.unshift(`${element.nodeName.toLowerCase()}:eq(${sibIndex})`);
        }
        else {
            stack.unshift(element.nodeName.toLowerCase());
        }

        element = element.parentNode;
    }

    return stack.join(" > ")
}

让我们假设您想为用户提供两种选择文本的选项。

  1. 页面中的简单文字。
  2. 选择输入文字或文本区域内的文字。
  3. 对于第一个选项,您可以使用带有单击处理程序或鼠标事件的按钮 为简单起见,我将使用一个按钮。

    function sendDataToServer(data) {
    }
    
    document.querySelector("#button").addEventListener("click", function (e) {
        var { target, text } = getSelectionTextAndContainerElement();
    
        var path = getPathFromElement(target);
    
        sendDataToServer({
            path: path,
            text: text 
        }); 
    });
    

    getSelectionTextAndContainerElement函数basicaly选择文本和元素。

    function getSelectionTextAndContainerElement() {
        var text;
        var containerElement = null;
    
        if (typeof window.getSelection !== "undefined") {
            var selection = window.getSelection();
    
            if (selection.rangeCount) {
                var node = selection.getRangeAt(0).commonAncestorContainer;
                containerElement = node.nodeType == 1 ? node : node.parentNode;
                text = selection.toString();
            }
        }
        else if (typeof document.selection !== "undefined" && document.selection.type !== "Control") {
            var textRange = document.selection.createRange();
    
            containerElement = textRange.parentElement();
            text = textRange.text;
        }
    
        return {
            text: text,
            target: containerElement
        };
    }
    

    对于第二个选项,您可以使用select事件处理程序。

    document.addEventListener("select", onSelect, false);
    
    function onSelect(e) {
        var { text } = getSelectionTextAndContainerElement();
        var path = getPathFromElement(e.target); 
    
        sendDataToServer({
            path: path,
            text: text 
        });       
    }
    

    对于输入文本或textarea,最好使用select事件处理程序 如果您将使用第一个选项来获取选择,则您将无法获得正确的目标节点,因为输入文本和textarea是使用Shadow DOM构建的。
    因此,最好忽略从getSelectionTextAndContainerElement函数重新生成的目标节点,并使用select事件的target属性。

    我已在jsfiddle为您创建了一个示例。

答案 2 :(得分:2)

第一个例子:

&#13;
&#13;
<textarea id="quote" cols="50" rows="5">
The above properties are especially useful in getting any user selected text from a form field where the indices of the selection isn't already known. The following demo echoes what the user has selected from a TEXTAREA using these properties:
</textarea>
 
<div id="output"></div>
 
<script>
 
var quotearea = document.getElementById('quote')
var output = document.getElementById('output')
quotearea.addEventListener('mouseup', function(){
    if (this.selectionStart != this.selectionEnd){ // check the user has selected some text inside field
        var selectedtext = this.value.substring(this.selectionStart, this.selectionEnd)
        output.innerHTML = selectedtext
    }
}, false)
 
</script>
&#13;
&#13;
&#13;

第二个例子

&#13;
&#13;
<head>
    <script type="text/javascript">
        function GetSelectedText () {
            var selText = "";
            if (window.getSelection) {  // all browsers, except IE before version 9
                if (document.activeElement && 
                        (document.activeElement.tagName.toLowerCase () == "textarea" || 
                         document.activeElement.tagName.toLowerCase () == "input")) 
                {
                    var text = document.activeElement.value;
                    selText = text.substring (document.activeElement.selectionStart, 
                                              document.activeElement.selectionEnd);
                }
                else {
                    var selRange = window.getSelection ();
                    selText = selRange.toString ();
                }
            }
            else {
                if (document.selection.createRange) {       // Internet Explorer
                    var range = document.selection.createRange ();
                    selText = range.text;
                }
            }
            if (selText !== "") {
                alert (selText);
            }
        }
    </script>
</head>
<body onmouseup="GetSelectedText ()">
    Some text for selection.
    <br /><br />
    <textarea>Some text in a textarea element.</textarea>
    <input type="text" value="Some text in an input field." size="40"/>
    <br /><br />
    Select some content on this page!
</body>
&#13;
&#13;
&#13;

第三个例子:

&#13;
&#13;
<head>
    <script type="text/javascript">
        function GetSelection () {
            var selection = "";

            var textarea = document.getElementById("myArea");
            if ('selectionStart' in textarea) {
                    // check whether some text is selected in the textarea
                if (textarea.selectionStart != textarea.selectionEnd) {
                    selection = textarea.value.substring  (textarea.selectionStart, textarea.selectionEnd);
                }
            }
            else {  // Internet Explorer before version 9
                    // create a range from the current selection
                var textRange = document.selection.createRange ();
                    // check whether the selection is within the textarea
                var rangeParent = textRange.parentElement ();
                if (rangeParent === textarea) {
                    selection = textRange.text;

                }
            }

            if (selection == "") {
                alert ("No text is selected.");
            }
            else {
                alert ("The current selection is: " + selection);
            }
        }
    </script>
</head>
<body>
    <textarea id="myArea" spellcheck="false">Select some text within this field.</textarea>
    <button onclick="GetSelection ()">Get the current selection</button>
</body>
&#13;
&#13;
&#13;

答案 3 :(得分:1)

我的想法是在所选文本的开头和结尾添加.csv,之后保存文档时整个html保存到数据库中,这样当他检索记录时,高亮显示的文本将保留。

&#13;
&#13;
<span >
&#13;
&#13;
&#13;

在jquery中添加元素会很舒服

答案 4 :(得分:1)

由于您使用插件进行文本突出显示,因此请使用jQuery获取突出显示的单词:

var words = $('.highlight').map(function() { return $(this).text(); });

然后将它们放入数组

var saved = [ ];
for (var word in words) {
    if (-1 === saved.indexOf(word)) {
        saved.push(word);
    }
}

最后,您可以将它们保存在数据库中。一个糟糕的(但很快)的方法是将列表保存为逗号分隔,着名的SQL antipattern

var wordList = saved.join(',');

检索值时,将其拆分为单词,并为每个单词调用高亮插件。

如果任何文本都包含逗号,则无效。在这种情况下,您最好单独保存每个单词,最后可以省去其他几个问题,而不是找出分离在用户文本中“不太可能”弹出的字符。

答案 5 :(得分:0)

您可以使用Array来保存用户的选择!!之后,将整个数组保存到数据库中!当用户再次查看该网站时,函数会比较数组中的字母和单词并突出显示它。

答案 6 :(得分:0)

您可以使用.serialize()方法返回标准URL编码表示法中的文本字符串。它已选择单个表单元素,例如<input>, <textarea>等。使用$(form).serialize();在数据库中推送序列化返回字符串,并突出显示更改检查旧$(form).serialize();返回值为新$(form).serialize();返回值。

答案 7 :(得分:0)

从测试的角度来看,如果可以在不调整高光的情况下改变原始html,则无法存储分离的高光。

我的解决方案是序列化整个彩色html。然后制作一个擦除功能,删除所有颜色高光并返回基线html。这允许数据库中的html包含颜色突出显示,并且仍然可以在突出显示时进行编辑。

类似的东西:

function unhighlight() {
  $('.highlighted').each(function(index, el) {
    $(el).replaceWith($(el).html());
  });
}

jsfiddle:https://jsfiddle.net/tobtedsc/5/