浏览器内文本的交互式选择突出显示
Interactive Selection highlighting of text inside the browser
我需要的是交互式 select通过 Javascript 在浏览器中突出显示文本。
更具体地说,假设我在 div
元素中有一堆文本,如下所示:
<div>The quick brown fox jumps over the lazy dog ... </div>
现在我要做的是 select 文本的某个范围并突出显示它(即 "brown fox")。我已经通过使用 rangy.js
.
实现了这个功能
现在我真的想让这个 selection / highlight 更具交互性,以便用户可以抓住 selection 的开头或结尾(使用某种句柄)并调整大小selection 通过将当前 selection 的开头或结尾拖动到新的起点或终点来交互 selection。
我试图 google 为此,但找不到任何东西。更糟糕的是,我完全不知道我应该如何开始实现这样的东西。
也许有人可以为我指出正确的方向,或者更好的是已经对如何开始实施这样的事情有了一些想法。
非常感谢您的帮助...
尝试
html
<div id="selectable"></div>
<br />
<hr />
<button id="clear">clear selections</button>
<hr />
<br />
<div id="selections">
<div class="selected"></div>
</div>
css
#selectable .ui-selecting {
background: #FECA40;
}
#selectable .ui-selected {
background: #F39814;
color: white;
}
#selections {
width: 100%;
height: 400px;
}
#clear {
position: relative;
left: calc(37%);
}
.selected {
width: 150px;
height: 150px;
overflow: hidden;
}
.selected,
.text {
display: block;
background-color: rgb(225, 225, 225);
}
.ui-widget-content {
border: 1px dotted rgba(170, 170, 170, .25);
}
.ui-widget-conent span {
display: inline-block;
padding: 4px;
margin: 4px;
}
js
var selectable = $("#selectable") // selectable words
, selections = $("#selections") // selected words container parent
, selected = $(".selected") // selected words container
, clear = $("#clear") // clear all
, sel = ".ui-selected" // selected word
, content = ".ui-widget-content" // selectable words element `class`
, text = ".text" // selected words draggable, resizable container `class`
, handles = {} // resize handles
, str = "The quick brown fox jumps over the lazy dog ..." // text, `string`
// split `str`, return array of words
, words = str.split(" ").map(function(word, i) {
$("<span />", {
"class": content.slice(1),
"text": word,
"css": {
"margin": "2px"
}
}).appendTo(selectable)
});
// append `handles` to `text` container
$.each(["nw", "ne", "sw", "se"], function(_, handle) {
var elem = $("<div />", {
"class": "ui-resizable-handle ui-resizable-" + handle,
"css": {
"width": "calc(12px + 5%)",
"height": "calc(12px + 5%)",
"border-radius": "50%",
"background": "#000"
}
})
.appendTo(selected)
.parent().find(":last")
.css({
"right": "-5px",
"bottom": "-5px"
});
handles[handle] = elem[0];
});
// `selected` settings
// set `selected` `display` to `none`
selected
.hide(0)
.resizable({
handles: handles
})
.draggable({
containment: "parent"
});
// collect selected words at `selectable.data("selections", [])` array,
// append words to `text`,
// append `text` to `selected`,
// set `selected` `display` to `block`
selectable.data("selections", [])
.selectable({
selected: function(event, ui) {
$(this).data("selections").push($(ui.selected).text())
},
stop: function() {
selected
.find(".text")
.remove()
.addBack()
.prepend(
$("<div />", {
"class": "text",
"text": $(this).data("selections").join(" "),
"css": {
"position": "relative",
"display": "block",
"padding": "calc(15%)",
"height": "calc(50%)",
"white-space": "pre-line",
"overflow": "hidden"
}
})
).show(0)
}
});
// remove selected words from `selectable.data("selections", [])` array,
// remove `sel` `class` from `selectable` words,
// remove `text`,
// set `selected` display to `none`
clear.on("click", function(e) {
selectable.data("selections", [])
.find(sel).removeClass(sel.slice(1));
selections.find(text).remove()
.addBack().find(selected).hide(0)
});
jsfiddle http://jsfiddle.net/guest271314/dpt9bn0n/
见jQueryUISelectable , Draggable , Resizable
$(function() {
var selectable = $("#selectable") // selectable words
, selections = $("#selections") // selected words container parent
, selected = $(".selected") // selected words container
, clear = $("#clear") // clear all
, sel = ".ui-selected" // selected word
, content = ".ui-widget-content" // selectable words element `class`
, text = ".text" // selected words draggable, resizable container `class`
, handles = {} // resize handles
, str = "The quick brown fox jumps over the lazy dog ..." // text, `string`
// split `str`, return array of words
, words = str.split(" ").map(function(word, i) {
$("<span />", {
"class": content.slice(1),
"text": word,
"css": {
"margin": "2px"
}
}).appendTo(selectable)
});
// append `handles` to `text` container
$.each(["nw", "ne", "sw", "se"], function(_, handle) {
var elem = $("<div />", {
"class": "ui-resizable-handle ui-resizable-" + handle,
"css": {
"width": "calc(12px + 5%)",
"height": "calc(12px + 5%)",
"border-radius": "50%",
"background": "#000"
}
})
.appendTo(selected)
.parent().find(":last") // `se`
.css({
"right": "-5px",
"bottom": "-5px"
});
handles[handle] = elem[0];
});
// `selected` settings
// set `selected` `display` to `none`
selected
.hide(0)
.resizable({
handles: handles
})
.draggable({
containment: "parent"
});
// collect selected words at `selectable.data("selections", [])` array,
// append words to `text`,
// append `text` to `selected`,
// set `selected` `display` to `block`
selectable.data("selections", [])
.selectable({
selected: function(event, ui) {
$(this).data("selections").push($(ui.selected).text())
},
stop: function() {
selected
.find(".text")
.remove()
.addBack()
.prepend(
$("<div />", {
"class": "text",
"text": $(this).data("selections").join(" "),
"css": {
"position": "relative",
"display": "block",
"padding": "calc(15%)",
"height": "calc(50%)",
"white-space": "pre-line",
"overflow": "hidden"
}
})
).show(0)
}
});
// remove selected words from `selectable.data("selections", [])` array,
// remove `sel` `class` from `selectable` words,
// remove `text`,
// set `selected` display to `none`
clear.on("click", function(e) {
selectable.data("selections", [])
.find(sel).removeClass(sel.slice(1));
selections.find(text).remove()
.addBack().find(selected).hide(0)
});
// do stuff
var phrase = [0, 1, 2, 8, 4, 5, 6, 7, 3, 9];
setTimeout(function() {
$.when(
selectable.queue("phrase", $.map(phrase, function(word, i) {
return function(next) {
return $.when(!$(this).find(content).eq(word)
.addClass("ui-selecting").parent()
.data("ui-selectable")._mouseStop(false) && $(this)
).then(function(el) {
return el.delay(2000);
}).then(next)
}
})
)
.dequeue("phrase").promise("phrase")
, clear
)
.then(function(el, button) {
button.trigger("click")
});
}, 2713);
});
#selectable .ui-selecting {
background: #FECA40;
}
#selectable .ui-selected {
background: #F39814;
color: white;
}
#selections {
width: 100%;
height: 400px;
}
#clear {
position: relative;
left: calc(37%);
}
.selected {
width: 150px;
height: 150px;
overflow: hidden;
}
.selected,
.text {
display: block;
background-color: rgb(225, 225, 225);
}
.ui-widget-content {
border: 1px dotted rgba(170, 170, 170, .25);
}
.ui-widget-conent span {
display: inline-block;
padding: 4px;
margin: 4px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<script src="http://code.jquery.com/ui/1.11.3/jquery-ui.js"></script>
<link href="http://code.jquery.com/ui/1.11.3/themes/smoothness/jquery-ui.css"
rel="stylesheet" />
<div id="selectable"></div>
<br />
<hr />
<button id="clear">clear selections</button>
<hr />
<br />
<div id="selections">
<div class="selected"></div>
</div>
这个问题相当复杂,并且由于缺乏跨浏览器的一致性而受到阻碍。但是,我已经成功解决了这个问题。
Test out my solution with this jsfiddle
我不保证这在所有情况下都有效,到目前为止我的测试仅限于文本。还有一些问题需要解决,我会在不久的将来解决。不过,我觉得这里已经准备好post了。
方法论
问题可以分解成几个步骤。首先,我们需要在用户的 selection 周围插入标记。这并不太难,尽管有几个必须解决的设计决策。经过一些实验,我决定将 span 直接插入文档中的标记位置;然后更容易 select 这些标记之间的内容。下面是插入标记的方法。
function insertMarker (isBefore) {
var range;
if (window.getSelection) {
// IE9+ and non-IE
var sel = window.getSelection();
if (sel.getRangeAt) {
range = window.getSelection().getRangeAt(0);
range.collapse(isBefore);
}
} else if (document.selection && document.selection.createRange) {
// IE < 9
range = document.selection.createRange();
range.collapse(isBefore);
}
// Create the marker element and insert it into the DOM.
if (range) {
range.insertNode(createMarker(isBefore));
}
}
其次,用户必须能够在文档周围拖动这些标记,以便它们锁定在 characters/selectable 内容之间。这可以使用 document.caretPositionFromPoint(x, y)
(标准)或 document.caretRangeFromPoint(x, y)
(WebKit)来实现。
第三,移动标记后,selection 必须更新以反映此更改。这可以使用 range.setStartAfter
和 range.setEndBefore
来实现,尽管 Range 实现不同,但大多数浏览器都支持它们。
function selectSelection () {
if (window.getSelection) {
// IE9+ and non-IE
var sel = window.getSelection();
var range = document.createRange();
range.setStartAfter($(".marker").get(0));
range.setEndBefore($(".marker").get(1));
sel.removeAllRanges();
sel.addRange(range);
}
else if (document.selection && document.selection.createRange) {
// IE < 9
var range = document.selection.createRange();
range.setStartAfter($(".marker").get(0));
range.setEndBefore($(".marker").get(1));
}
}
我需要的是交互式 select通过 Javascript 在浏览器中突出显示文本。
更具体地说,假设我在 div
元素中有一堆文本,如下所示:
<div>The quick brown fox jumps over the lazy dog ... </div>
现在我要做的是 select 文本的某个范围并突出显示它(即 "brown fox")。我已经通过使用 rangy.js
.
现在我真的想让这个 selection / highlight 更具交互性,以便用户可以抓住 selection 的开头或结尾(使用某种句柄)并调整大小selection 通过将当前 selection 的开头或结尾拖动到新的起点或终点来交互 selection。
我试图 google 为此,但找不到任何东西。更糟糕的是,我完全不知道我应该如何开始实现这样的东西。
也许有人可以为我指出正确的方向,或者更好的是已经对如何开始实施这样的事情有了一些想法。
非常感谢您的帮助...
尝试
html
<div id="selectable"></div>
<br />
<hr />
<button id="clear">clear selections</button>
<hr />
<br />
<div id="selections">
<div class="selected"></div>
</div>
css
#selectable .ui-selecting {
background: #FECA40;
}
#selectable .ui-selected {
background: #F39814;
color: white;
}
#selections {
width: 100%;
height: 400px;
}
#clear {
position: relative;
left: calc(37%);
}
.selected {
width: 150px;
height: 150px;
overflow: hidden;
}
.selected,
.text {
display: block;
background-color: rgb(225, 225, 225);
}
.ui-widget-content {
border: 1px dotted rgba(170, 170, 170, .25);
}
.ui-widget-conent span {
display: inline-block;
padding: 4px;
margin: 4px;
}
js
var selectable = $("#selectable") // selectable words
, selections = $("#selections") // selected words container parent
, selected = $(".selected") // selected words container
, clear = $("#clear") // clear all
, sel = ".ui-selected" // selected word
, content = ".ui-widget-content" // selectable words element `class`
, text = ".text" // selected words draggable, resizable container `class`
, handles = {} // resize handles
, str = "The quick brown fox jumps over the lazy dog ..." // text, `string`
// split `str`, return array of words
, words = str.split(" ").map(function(word, i) {
$("<span />", {
"class": content.slice(1),
"text": word,
"css": {
"margin": "2px"
}
}).appendTo(selectable)
});
// append `handles` to `text` container
$.each(["nw", "ne", "sw", "se"], function(_, handle) {
var elem = $("<div />", {
"class": "ui-resizable-handle ui-resizable-" + handle,
"css": {
"width": "calc(12px + 5%)",
"height": "calc(12px + 5%)",
"border-radius": "50%",
"background": "#000"
}
})
.appendTo(selected)
.parent().find(":last")
.css({
"right": "-5px",
"bottom": "-5px"
});
handles[handle] = elem[0];
});
// `selected` settings
// set `selected` `display` to `none`
selected
.hide(0)
.resizable({
handles: handles
})
.draggable({
containment: "parent"
});
// collect selected words at `selectable.data("selections", [])` array,
// append words to `text`,
// append `text` to `selected`,
// set `selected` `display` to `block`
selectable.data("selections", [])
.selectable({
selected: function(event, ui) {
$(this).data("selections").push($(ui.selected).text())
},
stop: function() {
selected
.find(".text")
.remove()
.addBack()
.prepend(
$("<div />", {
"class": "text",
"text": $(this).data("selections").join(" "),
"css": {
"position": "relative",
"display": "block",
"padding": "calc(15%)",
"height": "calc(50%)",
"white-space": "pre-line",
"overflow": "hidden"
}
})
).show(0)
}
});
// remove selected words from `selectable.data("selections", [])` array,
// remove `sel` `class` from `selectable` words,
// remove `text`,
// set `selected` display to `none`
clear.on("click", function(e) {
selectable.data("selections", [])
.find(sel).removeClass(sel.slice(1));
selections.find(text).remove()
.addBack().find(selected).hide(0)
});
jsfiddle http://jsfiddle.net/guest271314/dpt9bn0n/
见jQueryUISelectable , Draggable , Resizable
$(function() {
var selectable = $("#selectable") // selectable words
, selections = $("#selections") // selected words container parent
, selected = $(".selected") // selected words container
, clear = $("#clear") // clear all
, sel = ".ui-selected" // selected word
, content = ".ui-widget-content" // selectable words element `class`
, text = ".text" // selected words draggable, resizable container `class`
, handles = {} // resize handles
, str = "The quick brown fox jumps over the lazy dog ..." // text, `string`
// split `str`, return array of words
, words = str.split(" ").map(function(word, i) {
$("<span />", {
"class": content.slice(1),
"text": word,
"css": {
"margin": "2px"
}
}).appendTo(selectable)
});
// append `handles` to `text` container
$.each(["nw", "ne", "sw", "se"], function(_, handle) {
var elem = $("<div />", {
"class": "ui-resizable-handle ui-resizable-" + handle,
"css": {
"width": "calc(12px + 5%)",
"height": "calc(12px + 5%)",
"border-radius": "50%",
"background": "#000"
}
})
.appendTo(selected)
.parent().find(":last") // `se`
.css({
"right": "-5px",
"bottom": "-5px"
});
handles[handle] = elem[0];
});
// `selected` settings
// set `selected` `display` to `none`
selected
.hide(0)
.resizable({
handles: handles
})
.draggable({
containment: "parent"
});
// collect selected words at `selectable.data("selections", [])` array,
// append words to `text`,
// append `text` to `selected`,
// set `selected` `display` to `block`
selectable.data("selections", [])
.selectable({
selected: function(event, ui) {
$(this).data("selections").push($(ui.selected).text())
},
stop: function() {
selected
.find(".text")
.remove()
.addBack()
.prepend(
$("<div />", {
"class": "text",
"text": $(this).data("selections").join(" "),
"css": {
"position": "relative",
"display": "block",
"padding": "calc(15%)",
"height": "calc(50%)",
"white-space": "pre-line",
"overflow": "hidden"
}
})
).show(0)
}
});
// remove selected words from `selectable.data("selections", [])` array,
// remove `sel` `class` from `selectable` words,
// remove `text`,
// set `selected` display to `none`
clear.on("click", function(e) {
selectable.data("selections", [])
.find(sel).removeClass(sel.slice(1));
selections.find(text).remove()
.addBack().find(selected).hide(0)
});
// do stuff
var phrase = [0, 1, 2, 8, 4, 5, 6, 7, 3, 9];
setTimeout(function() {
$.when(
selectable.queue("phrase", $.map(phrase, function(word, i) {
return function(next) {
return $.when(!$(this).find(content).eq(word)
.addClass("ui-selecting").parent()
.data("ui-selectable")._mouseStop(false) && $(this)
).then(function(el) {
return el.delay(2000);
}).then(next)
}
})
)
.dequeue("phrase").promise("phrase")
, clear
)
.then(function(el, button) {
button.trigger("click")
});
}, 2713);
});
#selectable .ui-selecting {
background: #FECA40;
}
#selectable .ui-selected {
background: #F39814;
color: white;
}
#selections {
width: 100%;
height: 400px;
}
#clear {
position: relative;
left: calc(37%);
}
.selected {
width: 150px;
height: 150px;
overflow: hidden;
}
.selected,
.text {
display: block;
background-color: rgb(225, 225, 225);
}
.ui-widget-content {
border: 1px dotted rgba(170, 170, 170, .25);
}
.ui-widget-conent span {
display: inline-block;
padding: 4px;
margin: 4px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<script src="http://code.jquery.com/ui/1.11.3/jquery-ui.js"></script>
<link href="http://code.jquery.com/ui/1.11.3/themes/smoothness/jquery-ui.css"
rel="stylesheet" />
<div id="selectable"></div>
<br />
<hr />
<button id="clear">clear selections</button>
<hr />
<br />
<div id="selections">
<div class="selected"></div>
</div>
这个问题相当复杂,并且由于缺乏跨浏览器的一致性而受到阻碍。但是,我已经成功解决了这个问题。
Test out my solution with this jsfiddle
我不保证这在所有情况下都有效,到目前为止我的测试仅限于文本。还有一些问题需要解决,我会在不久的将来解决。不过,我觉得这里已经准备好post了。
方法论
问题可以分解成几个步骤。首先,我们需要在用户的 selection 周围插入标记。这并不太难,尽管有几个必须解决的设计决策。经过一些实验,我决定将 span 直接插入文档中的标记位置;然后更容易 select 这些标记之间的内容。下面是插入标记的方法。
function insertMarker (isBefore) {
var range;
if (window.getSelection) {
// IE9+ and non-IE
var sel = window.getSelection();
if (sel.getRangeAt) {
range = window.getSelection().getRangeAt(0);
range.collapse(isBefore);
}
} else if (document.selection && document.selection.createRange) {
// IE < 9
range = document.selection.createRange();
range.collapse(isBefore);
}
// Create the marker element and insert it into the DOM.
if (range) {
range.insertNode(createMarker(isBefore));
}
}
其次,用户必须能够在文档周围拖动这些标记,以便它们锁定在 characters/selectable 内容之间。这可以使用 document.caretPositionFromPoint(x, y)
(标准)或 document.caretRangeFromPoint(x, y)
(WebKit)来实现。
第三,移动标记后,selection 必须更新以反映此更改。这可以使用 range.setStartAfter
和 range.setEndBefore
来实现,尽管 Range 实现不同,但大多数浏览器都支持它们。
function selectSelection () {
if (window.getSelection) {
// IE9+ and non-IE
var sel = window.getSelection();
var range = document.createRange();
range.setStartAfter($(".marker").get(0));
range.setEndBefore($(".marker").get(1));
sel.removeAllRanges();
sel.addRange(range);
}
else if (document.selection && document.selection.createRange) {
// IE < 9
var range = document.selection.createRange();
range.setStartAfter($(".marker").get(0));
range.setEndBefore($(".marker").get(1));
}
}