Javascript保留格式翻译选区内容及预览(完结)
Javascript保留格式翻译选区内容及预览(完结)
前面的一篇文章Javascript保留格式翻译选区内容及预览(一),我们已经知道了,怎么获取选中的文字,然后交个接口返回,现在我们接着继续将这么讲翻译好的词语替换到原文的对应位置。 首先我们在选择文本并进行翻译时,我们的选区还在的,如果后续操作有存在丢掉选区的操作的话,我们需要先将选区暂存起来。
怎么保存选区
首先保存选区的前提是:选区还存在。
怎么说 ?
如果我们选中的词语是abc
,如果abc都被替换了,之前保存的选区信息也无法使用了,或者不准确了,就不要使用了。
const ranges = [];
for (let i = 0; i < window.getSelection().rangeCount; i++) {
const range = window.getSelection().getRangeAt(i);
ranges.push(range.cloneRange()); // clone后确保ranges可以用来重试
}
我们现在就把选区信息存储在`ranges`数组里面了
怎么恢复选区
既然提到保存选区,自然要知道怎么恢复选区了
// 恢复指定选区前移除所有可能存在选区
window.getSelection().removeAllRanges();
ranges.forEach(range => window.getSelection().addRange(range));
替换原文
替换原文的前提是保持原文处于选中状态,也就是保留选区,现在这一步上面已经做到了,再加上第一篇提到的获取的选区的节点的最近一层的共同父节点commonAncestorContainer
。现在我们正式开始进行替换了。
我们用两个方法完成替换,一个方法(traversalParent)是查找需要可能需要替换的dom节点。另一个方法(traversalReplace)进行替换。
寻找可能需要替换的dom节点
commonAncestorContainer
里面是包含我们需要替换的dom节点的,但是里面可能还有很多无关节点,所以我们必须进行排除。
这就需要window.getSelection().containsNode
方法,它能帮助我们判断某个节点是不是在选区中。
从MDN上看看它的api
我们可以判断指定节点是否全部或者部分包含在选区内。
如果dom全部内容都在选区是最简单的,直接全量替换了;如果只有不分在选区里面就麻烦点,比如原来的dom结构是
<div>你们好</div>
,但是用户只选中了“好”字,这时我们就不应该把“你们”给替换了。这部分工作就需要在我们的traversalReplace
方法里面实现了。
window.getSelection().anchorNode
和window.getSelection().focusNode
分别能让我们知道选区的起始和结尾的节点。因为我们是人工选择文本节点,所以我们这里的起始、结尾节点极大概率是文本节点或者是包含文本的元素节点(只是我们看不见而已,这时就属于全部包含了,反而简单)。
文本节点的data
属性会返回该文本的文字内容。
与anchorNode
和focusNode
对应的window.getSelection().anchorOffset
和window.getSelection().focusOffset
可以告知我们选择的文字在该文本节点的位置。
我们替换的的原文时就可以用
原文前内容+ 翻译结果 + 原文结尾内容来替换了。
这两个方法的代码如下
traversalParent(parent, isPreviewMode = false) {
let hit = 0;
let isReached = false;
const childNodes = parent.childNodes;
if (childNodes && childNodes.length > 0) {
for (let j = 0; j < parent.childNodes.length; j++) {
const node = parent.childNodes[j];
if (window.getSelection().containsNode(node)) {
isReached = true;
hit++;
traversalReplace(node, false, win)
} else if (window.getSelection().containsNode(node, true)) {
isReached = true;
hit++;
traversalReplace(node, true, win)
} else {
if (isReached) {
break;
}
}
}
} else if (parent.nodeType === 3) {
traversalReplace(parent, false, window);
}
}
traversalReplace(node, allowPartialContainment, win) {
const childNodes = node.childNodes;
if (childNodes && childNodes.length > 0) {
let isReached = false;
for (let i = 0; i < childNodes.length; i++) {
const item = childNodes[i];
// node部分包含时,再遍历它的子节点时,要继续判断子节点是否包含
if (allowPartialContainment && !window.getSelection().containsNode(item, true)) {
if (isReached) {
break;
}
} else {
isReached = true;
traversalReplace(item, allowPartialContainment, win);
}
}
} else if (node.nodeType === 3) {
const data = node.data || '';
if (data.trim().length < 1) {
return;
}
let targetText = '';
const translatedText = this.translatedWords[this.traversalIndex++] || '';
if (node === window.getSelection().anchorNode) {
// 存在node既是anchorNode又是focusNode的情况
const tailText = node === window.getSelection().focusNode ? node.data.slice(window.getSelection().focusOffset) : '';
targetText = node.data.slice(0, window.getSelection().anchorOffset) + translatedText + tailText;
} else if (node === window.getSelection().focusNode) {
targetText = translatedText + node.data.slice(window.getSelection().focusOffset);
} else {
targetText = translatedText;
}
// 此操作后会改变原来选中该node的Range的startOffset,endOffset等值。皮之不存毛将焉附
node.data = targetText;
}
}
具体请通过npm安装translate_selection
- 分类:
- Web前端
相关文章
Javascript保留格式翻译选区内容及预览(一)
目前市面上的不少翻译,一般场景比较简单,都是纯文本翻译(可能会包含换行\n之类的),但是最近遇到一个需求是要实现富文本里面的翻译,这里的翻译很大的概率会有格式,比如这种 我们需要带格式翻 阅读更多…