我正在与一个contenteditable一起工作,并试图创建一个简单的编辑器。不幸的是,我不能使用document.execCommand()
而必须自己实现。我在这里试图做的是,如果用户按下粗体按钮,我想使文本变为粗体。我在下面编写的代码有效,但是仅当选择位于一个节点而不是多个节点中时才起作用。
document.getElementById("bold").onclick = function() {
var selection = document.getSelection(),
range = selection.getRangeAt(0).cloneRange();
range.surroundContents(document.createElement("b"));
selection.removeAllRanges();
selection.addRange(range);
}
<div contenteditable="true" id="div">This is the editor. If you embolden only **this**, it will work. But if you try to embolden **this <i>and this**</i>, it will not work because they are in different nodes</div>
<button id="bold">Bold</button>
我的问题是:是否有一种解决方案,即使它们位于不同的节点中,我也可以单击粗体并且可以使文本加粗?如果是这样,我该怎么做?我正在寻找简单而优雅的东西,但是如果它必须很复杂,我希望对代码进行一些解释。非常感谢。
答案 0 :(得分:2)
这既不简单也不优雅,但是可以按预期工作,没有额外的标记,这是我能想到的最好的。
基本上,您必须遍历具有选择范围的dom树,并在遍历期间收集仅由文本节点组成的子范围。
查看Range documentation,以获取有关startContainer
和endContainer
的信息。
简而言之,选择一个文本节点时它们是相同的,否则它们将为您提供遍历的起点和终点。
一旦收集了这些范围,就可以将它们包装在自己喜欢的标签中。
效果很好,但不幸的是,加粗后我无法保留初始选择(用selection.setRange(..)
进行了所有尝试,但没有运气):
document.getElementById("bold").onclick = function() {
var selection = document.getSelection(),
range = selection.getRangeAt(0).cloneRange();
// start and end are always text nodes
var start = range.startContainer;
var end = range.endContainer;
var ranges = [];
// if start === end then it's fine we have selected a portion of a text node
while (start !== end) {
var startText = start.nodeValue;
var currentRange = range.cloneRange();
// pin the range at the end of this text node
currentRange.setEnd(start, startText.length);
// keep the range for later
ranges.push(currentRange);
var sibling = start;
do {
if (sibling.hasChildNodes()) {
// if it has children then it's not a text node, go deeper
sibling = sibling.firstChild;
} else if (sibling.nextSibling) {
// it has a sibling, go for it
sibling = sibling.nextSibling;
} else {
// we're into a corner, we have to go up one level and go for next sibling
while (!sibling.nextSibling && sibling.parentNode) {
sibling = sibling.parentNode;
}
if (sibling) {
sibling = sibling.nextSibling;
}
}
} while (sibling !== null && sibling.nodeValue === null);
if (!sibling) {
// out of nodes!
break;
}
// move range start to the identified next text node (sibling)
range.setStart(sibling, 0);
start = range.startContainer;
}
// surround all collected range by the b tag
for (var i = 0; i < ranges.length; i++) {
var currentRange = ranges[i];
currentRange.surroundContents(document.createElement("b"));
}
// surround the remaining range by a b tag
range.surroundContents(document.createElement("b"));
// unselect everything because I can't presere the original selection
selection.removeAllRanges();
}
<div contenteditable="true" id="div">This is the editor. If you embolden only **this**, it will work. If you try <font color="red">to embolden **this <i>and this**</i>, it will work <font color="green">because</font> we are traversing the</font> nodes<table rules="all">
<tr><td>it</td><td>will<td>even</td><td>work</td></tr>
<tr><td>in</td><td>more</td><td>complicated</td><td><i>markup</i></td></tr>
</table></div>
<button id="bold">Bold</button>
答案 1 :(得分:0)
function makeItBold() {
const x = document.querySelectorAll(".selected");
for (let i = 0; i < x.length; i++) {
x[i].style.fontWeight = "bold"
}
}
function makeItNormal() {
const x = document.querySelectorAll(".selected");
for (let i = 0; i < x.length; i++) {
x[i].style.fontWeight = "normal"
x[i].style.fontStyle = "normal"
}
}
function makeItItalic() {
const x = document.querySelectorAll(".selected");
for (let i = 0; i < x.length; i++) {
x[i].style.fontStyle = "italic"
}
}
<!DOCTYPE html>
<html>
<body>
<h4>A simple demonstration:</h4>
<button onclick="makeItBold()">Bold</button>
<button onclick="makeItNormal()">Normal</button>
<button onclick="makeItItalic()">Italic</button>
<p class="selected">Test me out</p>
</body>
</html>