NodeJS JavaScript函数的光标位置问题:由于鼠标指针自动移动到其他行,因此阻止代码编辑器中的文本编辑

uemypmqf  于 11个月前  发布在  Node.js
关注(0)|答案(1)|浏览(86)

bounty将在6天内到期。回答此问题可获得+50声望奖励。Nodigap正在寻找来自信誉良好的来源的**答案 *。

我正在用简单的JavaScript为HTML/CSS/js构建一个小而最小的代码编辑器。当你在里面写文本的时候会有一个问题。问题是我没有自由在任何我想写的地方写文本,因为无论我在哪里写/编辑,鼠标指针都会自动转移到第三行,而不是停留在我写的地方。
x1c 0d1x的数据
例如,如果我将鼠标指向编辑器中的某个地方并写一个单词,鼠标指针立即返回到另一行(总是在第三行的开头)。因此,它阻止我在我想写的地方写单词,而单词会写在其他地方。就好像在某些行的某些点上有块阻止你写,我将无法在我想写的地方写。

错误:最初,我认为错误是在HTML中,然后他们指出问题是在applyColor()函数中,也许是将光标位置设置为文本代码的结尾,我不知道。事实上,如果我删除applyColor()函数,那么问题就解决了,但结果是文本不再着色。但我需要这段代码(或者类似的东西),因为它是给文本着色的函数的一部分。因此我推断问题出在applyColor()函数上,尽管我不是100%确定。所以这应该是JavaScript的问题:

// CHANGE COLOR
  function changeColor() {
    var editor = document.getElementById("html");
    applyColoring(editor);
  
    // Set cursor position to the end of text
    var child = editor.children;
    var range = document.createRange();
    var sel = window.getSelection();
    range.setStart(child[child.length - 1], 1);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
    editor.focus();
  }

字符串

问题演示:我会在问题发生的时候演示给你看。在每一种情况下,鼠标指针都被带到了其他地方,我将无法在我想要的地方写入。我相信通过解决applyColor()函数中的unique问题,那么所有的问题都会自动解决,因为它们都依赖于同一个unique问题。

  • 如果我试着在第三行

    的开头写一个词,
  • 如果我试图在</h1>

    后面写一个单词,
  • 如果我尝试编辑This is a Heading,它会在<h1>This

    之间写入一个单词
  • 如果我试图在</p> x1c4d 1x后面写一个单词,
  • 如果我以前在第三行写过任何单词(例如aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

但是鼠标指针也可以在其他区域移动(在这3个五线谱内),我没有写过。当然,这一切都源于一个问题
我想要什么?**我希望当我写一个单词时(无论它写在哪里,在任何位置),鼠标指针应该保持静止/停在最后一个写的单词上(例如任何编辑器或聊天工作),并且不应该自动移动到第三行(如上面的照片)。当然,有些单词必须保持颜色(如我的代码中已经发生的那样)。例如,像这样,我想得到这样:



当然,如果我在HTML代码编辑器中插入新行,指针不会移动到第三行,而是会移动到第四行、第五行等等。
我怎样才能自由地在任何我想要的地方编写和修改编辑器代码,并避免将指针移动到其他行?那么,applicolor()函数应该如何修复?

P.S:我想手动设置标签、属性和括号的颜色,所以我不想使用REGEX,但我想使用我已经在使用的代码(或类似的东西)
index.html

<div id="html" contenteditable="true" oninput="showPreview();" onchange="changeColor();">&lt;h1>This is a Heading&lt;/h1>   
    &lt;p>This is a paragraph.&lt;/p>
  </div>
    
    <h3>PREVIEW</h3>
    <div class="preview-area">
      <iframe id="preview"></iframe>
    </div>
</div>

style.css

#html {
    width: 456px;
    height: 267px;
    padding: 10px;
    background-color: #444;
    color: white;
    font-size: 14px;
    font-family: monospace;
    white-space: pre;
  }
  .statement {
    color: orange;
  }
  
  #editor {
  
  }
  

#preview {
    display: block;
    width: 100%;
  }

JavaScript.js

function applyColoring(element) {
    var keywords = ["DIV", "SPAN", "H1", "P"];
    var keywords2 = ["CLASS", "ID"];
  
    var newHTML = "";
    // Loop through words
    str = element.innerText;
    (chunks = str
      .split(new RegExp(keywords.map((w) => `(${w})`).join("|"), "i"))
      .filter(Boolean)),
    (markup = chunks.reduce((acc, chunk) => {
      acc += keywords.includes(chunk.toUpperCase()) ?
        `<span class="statement">${chunk}</span>` :
        `<span class='other'>${chunk}</span>`;
      return acc;
    }, ""));
    element.innerHTML = markup;
  }
  
  // CHANGE COLOR
  function changeColor() {
    var editor = document.getElementById("html");
    applyColoring(editor);
  
    // Set cursor position to the end of text
    var child = editor.children;
    var range = document.createRange();
    var sel = window.getSelection();
    range.setStart(child[child.length - 1], 1);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
    editor.focus();
  }
     
  // PREVIEW
  const elHTML = document.querySelector("#html");
  //const elCSS = document.querySelector("#css");
  //const elJS = document.querySelector("#js");
  const elPreview = document.querySelector("#preview");
  
  function showPreview() {
    
    const html = elHTML.textContent;
    //const css = `<style>${elCSS.textContent}</style>`;
    //const js = `<scr` + `ipt>${elJS.textContent}</scr` + `ipt>`;
    const dataURL = "data:text/html;charset=utf-8," + encodeURIComponent(html);

    changeColor();
  
    elPreview.src = dataURL;
  }

  changeColor()
  showPreview()

axkjgtzd

axkjgtzd1#

您的主要问题在于您正在修改您正在键入的contenteditable DIV的内容,通过突然包含任意长度的HTML标记(突出显示SPAN元素)来更改其长度-然后试图猜测在哪里重置插入符号位置/索引。
如果我可以提出一个不同的方法:
创建一个可滚动的DIV Package 器.hilite
在它里面放置两个DIV:
一个contenteditable DIV(让我们将其归类为.hilite-editor),带有不可见的透明文本,但只有一个可见的插入符号(使用CSS caret-color)。
下面它放置另一个具有相同CSS样式的DIV(.hilite-colors),该DIV将纯粹用于显示色彩缤纷的语法<span>标记突出显示器元素:

<div class="hilite">
  <div class="hilite-colors"><!--JS generated HTML highlight markup--></div>
  <div class="hilite-editor" contenteditable=true><!--transparent text but visible caret--></div>
</div>

字符串
这样,您的插入符号将始终在contenteditable DIV内,因为你的类型。
然后,JS将(使用正则表达式或任何其他适当的方式)使用来自该contenteditable DIV的textContent,并动态创建突出显示的标记,该标记将在每个"input"事件的基础DIV .hilite-colors中结束。

const lang = {
  html: {
    tags: /(?<=&lt;(?:\/)?)(\w+)(?=\s|\&gt;)/g,
    angl: /(&lt;\/?|&gt;)/g,
    attr: /((?<=<span class="html-tags">\w+<\/span>)[^<]+)/g,
  },
  css: {}, // @TODO
  js: {}, // @TODO
};

const hilite = (el) => {
  const dataLang = el.dataset.lang; // "js", "css", "html", "py", "sh", ...
  const langObj = lang[dataLang]; // Extract object from lang regexes dictionary
  let html = el.innerHTML;
  Object.entries(langObj).forEach(([klass, regex]) => {
    html = html.replace(regex, `<span class="${dataLang}-${klass}">$1</span>`);
  });
  el.previousElementSibling.innerHTML = html; // Finally, show highlights!
};

const elHTML = document.querySelector(`[data-lang="html"]`);
const elCSS = document.querySelector(`[data-lang="css"]`);
const elJS = document.querySelector(`[data-lang="js"]`);
const elPreview = document.querySelector("#preview");

const preview = () => {  
  const encodedCSS = encodeURIComponent(`<style>${elCSS.textContent}</style>`);
  const encodedHTML = encodeURIComponent(elHTML.textContent);
  const encodedJS = encodeURIComponent(`<scr` + `ipt>${elJS.textContent}</scr` + `ipt>`); 
  const dataURL = `data:text/html;charset=utf-8,${encodedCSS + encodedHTML + encodedJS}`;
  elPreview.src = dataURL;
};

// Initialize!
[elHTML, elCSS, elJS].forEach((el) => {
  el.addEventListener("input", () => {
    hilite(el);
    preview();
  });
  hilite(el);
});
preview();
* {margin: 0; box-sizing: boder-box;}

/*scrollbar*/
::-webkit-scrollbar {
    width: 5px;
    height: 5px;
}

::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.1);
    border-radius: 0px;
}

::-webkit-scrollbar-thumb {
    background-color: rgba(255, 255, 255, 0.3);
    border-radius: 1rem;
}

body {
  font: 14px/1.4 sans-serif;
  background: hsl(220, 16%, 16%);
  color: #fff;
  padding: 16px;
}

#editor {
  display: flex;
}

h2 {
  padding: 16px 0;
  font-weight: 200;
  font-size: 14px;
}

.hilite {
  position: relative;
  background: hsl(220, 16%, 14%);
  height: 120px;
  overflow: auto;
}

.hilite-colors,
.hilite-editor {
  padding: 1rem;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  white-space: pre-wrap;
  font-family: monospace;
  font-size: 13px;
  min-height: calc(100% - 2rem);
}

/* THE OVERLAYING CONTENTEDITABLE WITH TRANSPARENT TEXT */
.hilite-editor {
  display: inline-block;
  position: relative;
  color: transparent; /* Make text invisible */
  caret-color: hsl( 50, 75%, 70%); /* But keep caret visible */
}
.hilite-editor:focus {
  outline: transparent;
}
.hilite-editor::selection {
  background: hsla( 0, 0%, 90%, 0.2);
}

/* THE UNDERLAYING DIV WITH HIGHLIGHT COLORS */
.hilite-colors {
  position: absolute;
  user-select: none;
}

/* HTML */
.html-angl { color: hsl(200, 10%, 45%); }
.html-tags { color: hsl(  0, 75%, 70%); }
.html-attr { color: hsl(200, 74%, 70%); }

/* JS */
/* @TODO */

/* CSS */
/* @TODO */

#preview {
  display: block;
  width: 100%;
  background: #fff;
  border: 0;
}
<h3>HTML</h3>
    
<div class="hilite">
  <div class="hilite-colors"></div>
  <div
    data-lang="html"
    class="hilite-editor"
    contenteditable="true"
    spellcheck="false"
    autocorrect="off"
    autocapitalize="off"
    >&lt;h1 class="heading">This is a Heading&lt;/h1>   
&lt;p>This is a paragraph.&lt;/p></div>
</div>
    
<h3>CSS</h3>
    
<div class="hilite">
  <div class="hilite-colors"></div>
  <div
    data-lang="css"
    class="hilite-editor"
    contenteditable="true"
    spellcheck="false"
    autocorrect="off"
    autocapitalize="off"
    >p {
  color: blue;
}</div>
</div>
    
<h3>JS</h3>
    
<div class="hilite">
  <div class="hilite-colors"></div>
  <div
    data-lang="js"
    class="hilite-editor"
    contenteditable="true"
    spellcheck="false"
    autocorrect="off"
    autocapitalize="off"
    ></div>
</div>

<h3>PREVIEW</h3>
    
<iframe id="preview"></iframe>

上面我创建的正则表达式完全是实验性的。2请随意修改它,并为CSS和JS添加缺少的正则表达式。
创建一个语法分析器和标记化工具会更好。进一步的阅读请参见VSCode syntax highlighting optimization,看看其他人是如何用惊人的解决方案解决这个问题的。
下面是上述代码的一个例子,其中有一个轻微的变化,并实现了https://highlightjs.org/库用于突出显示:Code editor using highlight.js
另一个库解决方案是:https://ace.c9.io/
关于more examples here

相关问题