首页 > 解决方案 > 语法荧光笔无法更新文本

问题描述

我正在尝试使用 HTML 制作代码编辑器:

<!DOCTYPE html>
<html lang='en'>
<head>
    <meta charset='UTF-8'>
    <meta http-equiv='X-UA-Compatible' content='IE=Edge'>
    <meta name='viewport' content='width=device-width, initial-scale=1'>
    <link rel='stylesheet' href='style.css'>
</head>

<body>
    <pre id='editor'><code contenteditable='true'></code></pre>
    
    <script type='module'>
        import { highlight } from './highlighter.js';
        import { Caret } from './caret.js';
        (() =>
        {
            const editor = document.querySelector('#editor code');
            const caret = new Caret(editor);
            highlight(editor);
            editor.addEventListener('input', e =>
            {
                highlight(editor);
                e.preventDefault();
            });
            editor.addEventListener('keydown', e =>
            {
                const TAB   = 9;
                const ENTER = 13;
                switch (e.keyCode)
                {
                    // ...
                }
            });
        })();
    </script>
</body>
</html>

荧光笔.js:

import { Caret } from './caret.js';
export function highlight(editor)
{
    // ...
    const NORM = '#E6E6FA';
    // ...
    const Highlighter = {
        source: editor.innerText,
        start: 0,
        curr: 0,
        // ...
        fin()
        {
            return this.curr >= this.source.length;
        },
        advance()
        {
            return this.source[this.curr++];
        },
        // ...
        scan()
        {
            let result = '';
            this.start = this.curr;
            if (this.fin())
            {
                return null;
            }
            const char = this.advance();
            let color = NORM;
            switch (char)
            {
                // ...
            }
            return {
                color,
                text: this.source.substring(this.start, this.curr),
            };
        },
    };
    let result = '';
    const caret = new Caret(editor);
    const save = caret.getPos();
    for (;;)
    {
        const lexeme = Highlighter.scan();
        if (lexeme === null)
        {
            break;
        }
        const chars = lexeme.text.split('').map(
            x => `<span style='color: ${lexeme.color};'>${x}</span>`);
        result += chars.join('');
    }
    editor.innerHTML = result;
    caret.setPos(save);
};

基本上,它获取用户代码的文本内容,对其进行扫描以生成带有颜色数据的词位,将这些词位拆分为带有颜色的 <span> 标记中的字符,然后将这些 <span> 附加到一个字符串中编辑器的innerHTML被更新为,最后将用户的光标放回原来的位置;这是在输入时完成的。但是有一个问题:如果用户输入速度太快,他们输入的文本可能会加倍。我尝试使用其他类型的事件侦听器来解决这个问题,并尝试简单地使用 setInterval,但它根本无法正常工作。

标签: javascripthtml

解决方案


从你所描述的

如果用户输入太快

highlight()并看到每次更改input元素时都会调用计算量大的函数

editor.addEventListener('input', e => {
  highlight(editor); // <--
  e.preventDefault();
});

我建议取消该调用以突出显示。这是 debouncing 的一个很好的解释器

尝试这样的事情:

// Vanilla debounce: https://gist.github.com/peduarte/7ee475dd0fae1940f857582ecbb9dc5f

function debounce(func, wait = 100) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, args);
    }, wait);
  };
}

// ...

// adjust delay to find a balance between responsiveness and performance
const delay = 500; 

const runHighlight = () => highlight(editor);
const debouncedHighlight = debounce(runHighlight, delay);

editor.addEventListener('input', e => {
  e.preventDefault();
  debouncedHighlight();
});

推荐阅读