如何解决文本操作:从剪贴板中检测替换
一般信息
致力于我自己的操作转换算法的实现。对于那些不知道这是什么的人:当多个用户同时处理同一个文档时,该算法会尝试保留每个用户的意图并确保所有用户最终得到相同的文档。
问题
首先,我需要一种检测文本操作的正确方法。就像插入和删除一样。显然,我需要确切地知道这发生在哪个位置,以便服务器可以正确转换每个操作以保留其他用户的意图。
到目前为止,我的代码在这方面做得相当不错。但是在选择文本范围并将其替换为另一个时会遇到麻烦。我为此依赖 input
事件,它似乎无法同时检测到删除和插入操作。执行此操作时,它会检测对所选文本的删除操作。但是它没有检测到剪贴板粘贴的文本的插入操作。
我的问题是:我该如何解决这个问题?
我的代码(到目前为止)
let txtArea = {};
let cursorPos = {};
let clientDoc = ""; // Shadow DOC
document.addEventListener("DOMContentLoaded",function(event){
txtArea = document.getElementById("test");
clientDoc = txtArea.value;
txtArea.addEventListener("input",function(){ handleinput(); });
txtArea.addEventListener("click",function(){ handleSelect(); });
});
/* Gets cursor position / selected text range */
function handleSelect(){
cursorPos = getCursorPos(txtArea);
}
/* Check whether the operation is insert or delete */
function handleinput(){
if(txtArea.value > clientDoc){
handleOperation("insert");
} else {
handleOperation("delete");
}
}
/* Checks text difference to kNow exactly what happened */
function handleOperation(operation){
let lines = "";
if(operation === "insert"){
lines = getDifference(clientDoc,txtArea.value);
} else if(operation === "delete"){
lines = getDifference(txtArea.value,clientDoc);
}
const obj = {
operation: operation,lines: lines,position: cursorPos
};
clientDoc = txtArea.value;
console.log(obj);
}
/* Simple function to get difference between 2 strings */
function getDifference(a,b)
{
let i = 0;
let j = 0;
let result = "";
while (j < b.length)
{
if (a[i] != b[j] || i == a.length){
result += b[j];
} else {
i++;
}
j++;
}
return result;
}
/* Function to get cursor position / selection range */
function getCursorPos(input) {
if ("selectionStart" in input && document.activeElement == input) {
return {
start: input.selectionStart,end: input.selectionEnd
};
}
else if (input.createTextRange) {
var sel = document.selection.createrange();
if (sel.parentElement() === input) {
var rng = input.createTextRange();
rng.movetoBookmark(sel.getBookmark());
for (var len = 0;
rng.compareEndPoints("EndToStart",rng) > 0;
rng.moveEnd("character",-1)) {
len++;
}
rng.setEndPoint("StartToStart",input.createTextRange());
for (var pos = { start: 0,end: len };
rng.compareEndPoints("EndToStart",-1)) {
pos.start++;
pos.end++;
}
return pos;
}
}
return -1;
}
#test {
width: 600px;
height: 400px;
}
<textarea id="test">test</textarea>
解决方法
设法自己解决了问题,但不完全确定这是否是最佳解决方案。我在代码中使用了注释来解释我是如何解决它的:
function handleOperation(operation){
let lines = "";
if(operation === "insert"){
lines = getDifference(clientDoc,txtArea.value);
} else if(operation === "delete"){
lines = getDifference(txtArea.value,clientDoc);
}
// This handles situations where text is being selected and replaced
if(operation === "delete"){
// Create temporary shadow doc with the delete operation finished
const tempDoc = clientDoc.substr(0,cursorPos.start) + clientDoc.substr(cursorPos.end);
// In case the tempDoc is different from the actual textarea value,we know for sure we missed an insert operation
if(tempDoc !== txtArea.value){
let foo = "";
if(tempDoc.length > txtArea.value.length){
foo = getDifference(txtArea.value,tempDoc);
} else {
foo = getDifference(tempDoc,txtArea.value);
}
console.log("char(s) replaced detected: "+foo);
}
} else if(operation === "insert"){
// No need for a temporary shadow doc. Insert will always add length to our shadow doc. So if anything is replaced,// the actual textarea length will never match
if(clientDoc.length + lines.length !== txtArea.value.length){
let foo = "";
if(clientDoc.length > txtArea.value.length){
foo = getDifference(txtArea.value,clientDoc);
} else {
foo = getDifference(clientDoc,txtArea.value);
}
console.log("char(s) removed detected: "+foo);
}
}
const obj = {
operation: operation,lines: lines,position: cursorPos
};
// Update our shadow doc
clientDoc = txtArea.value;
// Debugging
console.log(obj);
}
如果你能给我更好的解决方案/技巧/建议,我仍然非常愿意接受。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。