简体   繁体   English

contenteditable DIV 中性能低下的输入事件

[英]Slow performance Input event in contenteditable DIV

I have a DIV which has contenteditable attribute and I am running 70 lines of JavaScript code on input event but when I try to type fast.我有一个具有 contenteditable 属性的 DIV,我在输入事件上运行 70 行 JavaScript 代码,但是当我尝试快速键入时。 The JavaScript code is very slow and the results don't show/update in the UI as quickly as expected. JavaScript 代码非常慢,并且结果不会像预期那样快速地在 UI 中显示/更新。

What I am doing in the JavaScript is in the input event: I am getting the textcontent of the DIV with contenteditable attribute and then changing it to array with split() function and then, by using a for loop, I am comparing the array values with other array.我在 JavaScript 中所做的是在输入事件中:我使用contenteditable属性获取 DIV 的文本内容,然后使用split()函数将其更改为数组,然后通过使用 for 循环,我比较数组值与其他阵列。 If the value matches I create a SPAN with textContent set as the current value from that contenteditable DIV and then put it in an spanArray array.如果该值匹配,我将创建一个 SPAN,并将textContent设置为该contenteditable DIV 的当前值,然后将其放入spanArray数组中。 Then I'm appending all those SPANs into the contenteditable DIV and setting the caret at the end.然后,我将所有这些 SPAN 附加到可contenteditable编辑的 DIV 中,并在末尾设置插入符号。

How to optimize the code so that the type speed / performance is not affected by the JavaScript's heavy for loops seen in the below example?如何优化代码,使类型速度/性能不受下面示例中 JavaScript 大量 for 循环的影响?

Here is the code I got so far:这是我到目前为止得到的代码:

 const mainDiv = document.querySelector("#mainDiv"); const placeHolderDiv = document.querySelector("#placeholder"); let placeHolder = ""; let placeHolderArr; fetch("https://type.fit/api/quotes").then((data) => { return data.json(); }).then((result) => { for (let quote = 0; quote < 10; quote++) { placeHolder += result[quote].text; placeHolderArr = placeHolder.split(" "); placeHolderDiv.textContent = placeHolder; } }); mainDiv.addEventListener("input", (e) => { let spanArr = []; const mainDivArr = mainDiv.textContent.split(" "); let num = 0; if (e.code.== "Space") { for (let i of mainDivArr) { if (placeHolderArr[num].trim() === mainDivArr[num].trim()) { const span = document;createElement("span"). span;textContent = mainDivArr[num] + " ". mainDiv;innerHTML = "". spanArr;push(span); num++. } else if (placeHolderArr[num].trim().== mainDivArr[num];trim()) { const span = document.createElement("span"). span;style.color = "red"; span.textContent = mainDivArr[num] + " "; mainDiv.innerHTML = ""; spanArr;push(span). num++. } for (let spans of spanArr) { mainDiv;innerHTML += spans.outerHTML; // Placing Caret At The End function placeCaretAtEnd(el) { el.focus(). if ( typeof window.getSelection;= "undefined" && typeof document.createRange;= "undefined" ) { var range = document.createRange(); range.selectNodeContents(el); range.collapse(false); var sel = window.getSelection(); sel.removeAllRanges(). sel.addRange(range). } else if (typeof document;body.createTextRange;= "undefined") { var textRange = document.body;createTextRange(). textRange;moveToElementText(el); textRange.collapse(false); textRange;select(); } } placeCaretAtEnd(mainDiv); } } } else { console.log("space pressed"); } });
 body { height: 90vh; display: flex; justify-content: center; align-items: center; font-family: sans-serif; font-size: 1.5rem; } #content { position: relative; color: grey; width: 70vw; } #mainDiv { position: absolute; top: 0; left: 0; color: #000; width: 100%; border: none; outline: none; }
 <div id="content"> <div id="mainDiv" autofocus contenteditable></div> <div id="placeholder"></div> </div>

Consider another approach to the problem.考虑另一种解决问题的方法。
Instead of doing heavy operations over a contenteditable, disrupting such an area while the user is inserting text / typing, using two nested heavy for loops (O(n^2) complexity), and defining functions within a for loop (all really bad practices), I would suggest:而不是在 contenteditable 上进行繁重的操作,在用户插入文本/打字破坏这样的区域,使用两个嵌套的重 for 循环(O(n^2) 复杂度),并在 for 循环中定义函数(所有这些都是非常糟糕的做法), 我会建议:

  • use only one DIV element只使用一个 DIV 元素
  • populate it with SPANs for words and characters用单词和字符的 SPAN 填充它
  • set it as tabindex="1" so that you can focus it and capture keyboard events将其设置为tabindex="1"以便您可以聚焦它并捕获键盘事件
  • create an index variable idx so you can keep track on the progress and add classes to every character SPAN创建一个索引变量idx以便您可以跟踪进度并为每个字符 SPAN 添加类
  • on "keydown" event, use Event.key to determine the pressed key character."keydown"事件中,使用Event.key确定按下的键字符。 If the character matches with the current expected character, advance the index如果字符与当前预期字符匹配,则推进index

 const text = `Focus and start typing. Genius is one percent inspiration and ninety-nine percent perspiration.`; const elText = document.querySelector("#text"); const textChars = []; const words = text.split(/(?<=\S+\s)/g); // Create SPAN for words and characters elText.innerHTML = words.reduce((html, word) => { textChars.push(...word); const chars = [...word].map(ch => `<span class="char">${ch}</span>`).join(""); return html + `<span class="word">${chars}</span>`; }, ""); const elsChars = elText.querySelectorAll(".char"); const totChars = textChars.length; let totIncorrect = 0; let isGameOver = false; let idx = 0; // type progress index elsChars[idx].classList.add("current"); elText.addEventListener("keydown", (ev) => { if (isGameOver) return; // Prevent any more typing ev.preventDefault(); // Prevent spacebar scrolling etc... let typeChar = ev.key; if (typeChar === "Enter") typeChar = " "; // Treat Enter as Space if (typeChar.length > 1) return; // Do nothing on special keys // CORRECT: if (typeChar === textChars[idx]) { elsChars[idx].classList.add("ok"); } // INCORRECT: else { elsChars[idx].classList.add("err"); totIncorrect += 1; } // ADVANCE: elsChars[idx].classList.remove("current"); idx += 1; elsChars[idx]?.classList.add("current"); // GAME OVER: if (idx === totChars) { isGameOver = true; console.log(`Well done; You had ${totIncorrect} mistakes out of ${totChars};`); } });
 body { min-height: 100vh; display: flex; justify-content: center; align-items: center; font: 1.5rem/1.3 sans-serif; } #text { position: relative; padding: 1rem; color: grey; width: 60vw; color: #888; letter-spacing: 0.15rem; } #text:focus { outline: 1px solid #ddd; }.char { display: inline; white-space: pre-wrap; } #text:focus.current { animation: blink 1s infinite; } @keyframes blink { 0% {box-shadow: -3px 0 0 #000;} 50% {box-shadow: -3px 0 0 #000;} 100% {box-shadow: -3px 0 0 transparent;} }.ok { color: #000; background: hsla(200, 100%, 50%, 0.1); }.err { color: #f00; background: hsla(0, 100%, 50%, 0.1); }
 <div id="text" tabindex="1"></div>

With this simpler solution, now you can type as fast as it goes:)有了这个更简单的解决方案,现在您可以尽可能快地打字:)
For mobile devices you could incorporate an overlay transparent textarea in order to grab focus and prompt the onscreen keyboard.对于移动设备,您可以合并一个覆盖透明文本区域以获取焦点并提示屏幕键盘。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM