我有一个内容可编辑的div,我希望用户输入。当用户使用onkeyup在框中键入内容时,我激活了一个更改某些字符颜色的函数:

var lTags = fixedCode.innerHTML.replace(/&lt;/gi, "<span style='color:gold;'>&lt;</span>");
var rTags = lTags.replace(/&gt;/gi, "<span style='color:gold'>&gt;</span>");
fixedCode.innerHTML = rTags;

该代码的作用是将每个符号转换为金色。但是,当我这样做时,我不再能够在contenteditable框中键入单词,因为每次按下一个键时,该框都会自动刷新。

function checkIt(code) {
   var fixedCode = document.getElementById(code);
   var lTags = fixedCode.innerHTML.replace(/&lt;/gi, "<span style='color:gold;'>&lt;</span>");
   var rTags = lTags.replace(/&gt;/gi, "<span style='color:gold'>&gt;</span>");
   fixedCode.innerHTML = rTags;
}
<div id="box" contenteditable="true" onkeyup="checkIt(this.id);">See for yourself</div>


要亲自查看,请尝试在框中键入任何HTML标记。
首先,为什么它会更改标签左<而不是标签>右部分的颜色?以及如何在不删除变色 Material 的情况下在框内输入内容。我见过类似的问题,但答案是Jquery。 我不想使用JQUERY!

最佳答案

这个示例目前可以在Chrome中使用进行工作 ...,但是可以找到一种方法,即使文本是透明的,也可以使插入符号可见-在其他浏览器上,您一切顺利。

所以我对Hardcore-JS太懒了,一个让我大吃一惊的想法是使用

  • 两个重叠的DIV元素
  • 可叠加的内容可编辑具有透明文本,但可见的插入符号!
  • 底层的DIV是用来显示您替换的彩色内容的:

  • 关于此技术的一个好的一面是,您始终(在contenteditable DIV中)保留要替换的内容的未更改版本!
    不好的一面是,在每次击键时,您都再次解析相同的内容-随着替换列表的增加,它可能会成为性能杀手,因为替换列表会增加O(n)

    有关优化的详细信息,请阅读前往VSCode Optimizations in Syntax Highlighting

    基本示例:

    function highLite(el) {
      el.previousElementSibling.innerHTML = el.innerHTML
         .replace(/(&lt;|&gt;)/g, "<span class='hl_angled'>$1</span>")
         .replace(/(\{|\})/g, "<span class='hl_curly'>$1</span>");
    }
    body{margin:0; font:14px/1 sans-serif;}
    
    .highLite{
      border: 1px solid #888;
      position: relative;
    }
    
    .highLite_colors,
    .highLite_editable {
      padding: 16px;
    }
    
    /* THE UNDERLAYING ONE WITH COLORS */
    .highLite_colors {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      user-select: none;
    }
    
    /* THE OVERLAYING CONTENTEDITABLE WITH TRANSPARENT TEXT */
    .highLite > div:nth-child(2) {
      position: relative;
      -webkit-text-fill-color: transparent; /* Chrome: make text transparent */
      text-fill-color: transparent;         /* One day, hopefully? */
      color: black;                         /* But keep caret black */
    }
    
    .hl_angled{ color: turquoise; }
    .hl_curly{ color: fuchsia; }
    Try to type some angled &lt; &gt; or curly { } brackets
    <div class="highLite">
      <div class="highLite_colors">Type here:</div>
      <div class="highLite_editable" contenteditable oninput="highLite(this);">Type here:</div>
    </div>


    高级示例:

    (需要您自担风险,这是我的咖啡时间游乐场)

    const lang = {
      js: {
        equa: /(\b=\b)/g,
        quot: /(`|'|"|&#39;|&#34;)/g,
        comm: /((\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\/)|(\/\/.*))/g,
        logi: /(%=|%|\-|\+|\*|&amp;{1,2}|\|{1,2}|&lt;=|&gt;=|&lt;|&gt;|!={1,2}|={2,3})/g,
        numb: /(\d+(\.\d+)?(e\d+)?)/g,
        func: /(?<=^|\s*)(async|console|alert|Math|Object|Array|String|class(?!\s*\=)|function|(?<=\.)\D\w*)(?=\b)/g,
        decl: /(?<=^|\s*)(var|let|const)/g, // Declarations
        pare: /(\(|\))/g,
        squa: /(\[|\])/g,
        curl: /(\{|\})/g,
      },
      // Props order matters! Here I rely on "tags:"
      // being already applied in the previous iteration
      html: {
        tags: /(?<=&lt;(?:\/)?)(\w+)(?=\s|\&gt;)/g,
        angl: /(&lt;\/?|&gt;)/g,
        attr: /((?<=<i class=html_tags>\w+<\/i>)[^<]+)/g,
      }
    };
    
    const highLite = el => {
      const dataLang = el.dataset.lang; // Detect "js", "html", "py", "bash", ...
      const langObj = lang[dataLang]; // Extract object from lang regexes dictionary
      let html = el.innerHTML;
      Object.keys(langObj).forEach(function(key) {
        html = html.replace(langObj[key], `<i class=${dataLang}_${key}>$1</i>`);
      });
      el.previousElementSibling.innerHTML = html; // Finally, show highlights!
    };
    
    const editors = document.querySelectorAll(".highLite_editable");
    editors.forEach(el => {
      el.contentEditable = true;
      el.spellcheck = false;
      el.autocorrect = "off";
      el.autocapitalize = "off";
      el.addEventListener("input", highLite.bind(null, el));
      el.addEventListener("input", highLite.bind(null, el));
      highLite(el); // Init!
    });
    * {margin: 0; box-sizing: boder-box;}
    
    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;
    }
    
    .highLite {
        position: relative;
        background: hsl(220, 16%, 14%);
    }
    
    .highLite_colors,
    .highLite_editable {
        padding: 16px;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        white-space: pre-wrap;
        font-family: monospace;
        font-size: 13px;
    }
    
    /* THE OVERLAYING CONTENTEDITABLE WITH TRANSPARENT TEXT */
    .highLite_editable {
        position: relative;
        -webkit-text-fill-color: transparent; /* Chrome: make text transparent */
        text-fill-color: transparent; /* One day, hopefully? */
        color: hsl(100, 95%, 95%); /* But keep caret colored */
    }
    .highLite_editable:focus {
        outline: 1px solid hsl(220, 16%, 19%);
    }
    
    /* THE UNDERLAYING ONE WITH HIGHLIGHT COLORS */
    .highLite_colors {
        position: absolute;
        user-select: none;
    }
    
    .highLite_colors i {
        font-style: normal;
    }
    
    /* JS */
    i.js_quot { color: hsl( 50, 75%, 70%); }
    i.js_decl { color: hsl(200, 75%, 70%); }
    i.js_func { color: hsl(300, 75%, 70%); }
    i.js_pare { color: hsl(210, 75%, 70%); }
    i.js_squa { color: hsl(230, 75%, 70%); }
    i.js_curl { color: hsl(250, 75%, 70%); }
    i.js_numb { color: hsl(100, 75%, 70%); }
    i.js_logi { color: hsl(200, 75%, 70%); }
    i.js_equa { color: hsl(200, 75%, 70%); }
    i.js_comm { color: hsl(200, 10%, 45%); font-style: italic; }
    i.js_comm > * { color: inherit; }
    
    /* HTML */
    i.html_angl { color: hsl(200, 10%, 45%); }
    i.html_tags { color: hsl(  0, 75%, 70%); }
    i.html_attr { color: hsl(200, 74%, 70%); }
    <h2>HTML</h2>
    <div class="highLite">
        <div class="highLite_colors"></div>
        <div class="highLite_editable" data-lang="html">&lt;h2 class="head"&gt;
    TODO: HTML is for &lt;b&gt;homework&lt;/b&gt;
    &lt;/h2&gt;</div>
    </div>
    
    <h2>JAVASCRIPT</h2>
    <div class="highLite">
        <div class="highLite_colors"></div>
        <div class="highLite_editable" data-lang="js">// Type some JavaScript here
    
    const arr = ["high", "light"];
    let n = 2.1 * 3;
    if (n &lt; 10) {
      console.log(`${n} is &lt;&#61; than 10`);
    }
    function casual (str) {
      str = str || "nonsense";
      alert("Just a casual"+ str +", still many TODOs");
    }
    casual (arr.join('') +" idea!");
    
    /**
    * The code is a proof of concept and far from
    * perfect. You should never use regex but create or use a parser.
    * Meanwhile, play with it and improve it!
    */</div>
    </div>


    有了这个基本思想,我留给读者的一个简单的 TODO 是-当我们正在编辑的DIV收到滚动条时,使用JavaScript相应地更新同级DIV的滚动位置。

    07-24 09:38
    查看更多