我遇到了html和javascript的困境。 我的html页面允许用户选择文本并用颜色突出显示。现在我想将状态保存到数据库中以便稍后为该用户显示它。当然,我可以在用户编辑后保存整个html。但我只想保存一些参数,结合原始html显示状态用户上次查看的页面。我们可以使用这个功能:
var index = innerHTML.indexOf(text);
突出显示该索引处的文字。但是如果页面中有很多相同的文本,我想要突出显示之前用户突出显示的文字。
任何人都可以通过javascript告诉我如何实现这一目标?
我非常感谢你的帮助。
答案 0 :(得分:7)
Range
个对象和document.execCommand
允许很容易地操纵选择。您的主要问题是以文本格式保存范围对象。
基本上您需要的是获取startContainer
,startOffset
,endContainer
和endOffset
,它们是创建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(" > ")
}
让我们假设您想为用户提供两种选择文本的选项。
对于第一个选项,您可以使用带有单击处理程序或鼠标事件的按钮 为简单起见,我将使用一个按钮。
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)
第一个例子:
<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;
第二个例子
<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;
第三个例子:
<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;
答案 3 :(得分:1)
我的想法是在所选文本的开头和结尾添加.csv
,之后保存文档时整个html保存到数据库中,这样当他检索记录时,高亮显示的文本将保留。
<span >
&#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/