工作中需要使用富文本,出于简洁的考虑,我选择了wangeditor,可是PM突然说要加一个格式刷功能,所以去找插件,然后我就看到了下面这张图。
image同时也在其他地方看到了类似的文章,不过他使用的editor版本貌似和我的不一致,所以决定自己写一个。
首先要判断格式刷的边界问题,range获取到的节点是当前选中区域所在节点的父节点,而不是你选择的文字部分。所以,实际上需要复制的样式是获取到的节点一层一层往里找,直到找到文本节点为止,如果该节点没有,就找它的第一个子节点。
function getTargetDom(dom) {
for (let i of dom.childNodes) {
if (i.nodeType === 3 && i.nodeValue && i.nodeValue.trim() !== '') {
targetDom = dom;
return;
}
}
getTargetDom(dom.children[0]);
}
然后我们要从这个目标节点开始一层一层往上找,直到找到p
标签为止,因为p标签在富文本里面代表一行,同时,因为模板没有严格按照编辑器里面来,我们需要把p
标签里面的样式也做一个处理,这里我换成了span
标签。
function getAllStyle(dom) {
if (!dom) return;
const tagName = dom.tagName.toLowerCase();
if (tagName === 'p') {
nodeArray.push({
tagName: 'span',
attributes: Array.from(dom.attributes).map((i) => {
return {
name: i.name,
value: i.value,
};
}),
});
return;
} else {
nodeArray.push({
tagName: tagName,
attributes: Array.from(dom.attributes).map((i) => {
return {
name: i.name,
value: i.value,
};
}),
});
getAllStyle(dom.parentNode);
}
}
return nodeArray;
}
最后一步,就是把从内到外获取到的节点和属性,按原样套接上去得到最后的节点
function addStyle(text, nodeArray) {
let currentNode = null;
nodeArray.forEach((ele, index) => {
let node = document.createElement(ele.tagName);
for (const attr of ele.attributes) {
node.setAttribute(attr.name, attr.value);
}
if (index === 0) {
node.innerText = text;
currentNode = node;
} else {
node.appendChild(currentNode);
currentNode = node;
}
});
return currentNode;
}
接下来是和编辑器相关的操作就不在赘述了,结合编辑器以及MDN的文档,可以实现大部分功能。
值得一提的是,我这里判断文字选择用的是mouseup
的事件,因为暂时也没想到其他的办法,如果有更好的办法,希望不吝赐教。
预览地址:https://snjiang1992.github.io/format-painter/
源码地址:https://github.com/SNJiang1992/format-painter
网友评论