在 Javascript 中,当用户键入时突出显示设置单词,同时保持光标位置[关闭]

问题描述 投票:0回答:1

我正在尝试在网页上制作一个超级简单的编辑器,当用户输入文本时,它会自动突出显示大型猫科动物的名字。

脚本要点如下:

  1. 当用户点击空格键时,将根据数组检查最后输入的单词。如果找到匹配项,该单词将被包裹在
    <span>
  2. 如果用户更改已包含在
    <span>
    中的单词并点击空格键,并且更改后的单词不匹配,则应删除
    <span>
  3. 无论添加还是删除
    <span>
    突出显示,任何现有的 HTML 标签(例如单词周围的
    <strong>
    标签)都应保留。
  4. 检查后光标位置不应改变。

请注意,我不能使用像 mark.js 这样的匹配库;这应该只在用户点击空格键后检查刚刚输入的单词,并且只应在该特定单词上进行检查。

我的原始脚本按预期工作,但仅满足前两个要求。后两者已经接近我的 JS 技能极限了。

我有以下 HTML:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
Type some words. The names of common big cats should highlight
<div id='myDiv' contenteditable='true' spellcheck='false' style='width:500px;height:300px;border:1px solid red;'></div>

我的CSS非常简单:

.matchCat{position: relative;display: inline-block;color:red;}

还有我的 Javascript/Jquery:

const catArray = ['lion','tiger','cheetah','leopard','puma'];
    const catInput = $('#myDiv');
    
    catInput.keyup(function(e) {
        if(e.keyCode == 32){
            var words = $(this).text().split(' '); // grab user contents and split it
            var target_word = words[words.length - 1]; // get the last word of the input
            target_word = target_word.trim(); // trim excess spaces
            target_word = target_word.replace(/[^\w\s]|_/g, ''); // get just the actual word and no punctutation
            if ( $.inArray(target_word, catArray) > -1 ) {
                change_cat(target_word, "<span class='matchCat'>"+target_word+"</span>");
            } else {
                change_cat("<span class='matchCat'>"+target_word+"</span>", target_word);
            }
        }
    });

    function change_cat(from,to){   
        catInput.each(function(){
            var text = $(this).text();
            text = text.replace(from,to);
            $(this).html(text);
        });
    }

JSFiddle 版本在这里:Mark Cats

此代码无法正常工作,原因如下:

  1. 当您第一次输入出现在数组中的大型猫科动物的名称时,该单词会正确突出显示,但光标会跳回可编辑窗口的开头。按下空格键后它应该保持在原来的位置。
  2. 如果您输入“tiger”,然后使用 Ctrl+B 将其加粗,然后再按空格键,则猫会突出显示,但它会失去样式。理想情况下
    <strong>tiger</strong>
    应该变成
    <span class='markCat'><strong>tiger</strong></span>
    ,但我的替换命令会丢失现有样式。
  3. 键入匹配的多个实例似乎失败。如果您输入“tiger”,当您点击空格时它会突出显示。但是,如果您转到输入的末尾并再次输入(例如“tiger Tiger”),那么突出显示就会关闭。那不应该发生。所有“老虎”实例都应突出显示。

编辑:根据此处收到的帮助,我有代码的第二个版本,可以在此处的 JSFiddle 中看到:Mark Cats v2

这比我的代码更好,但仍然无法正常运行,因为在空格键检查后,当光标应保留在按下空格键后的字符位置的窗口中时,它会失去焦点。 第二个问题是在正确突出显示找到的单词之后,如果我返回并更改该单词,因此没有匹配项,然后再次按空格,突出显示不会消失。

javascript jquery string-matching
1个回答
0
投票

尝试将您的 jquery 更新为以下内容

const catArray = ['lion', 'tiger', 'cheetah', 'leopard', 'puma'];
const catInput = $('#myDiv');

// Function to save cursor position
function saveCaretPosition(context) {
    let selection = window.getSelection();
    let range = selection.getRangeAt(0);
    range.setStart(context, 0);
    const len = range.toString().length;

    return function restore() {
        let pos = getTextNodeAtPosition(context, len);
        selection.removeAllRanges();
        let newRange = new Range();
        newRange.setStart(pos.node, pos.position);
        selection.addRange(newRange);
    }
}

// Function to get text node at position
function getTextNodeAtPosition(root, index) {
    const treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
    let node;
    let count = 0;

    while (node = treeWalker.nextNode()) {
        count += node.textContent.length;
        if (count >= index) {
            return {
                node: node,
                position: node.textContent.length + index - count
            };
        }
    }
    return { node: null, position: 0 };
}

catInput.on('keyup', function(e) {
    if (e.keyCode === 32) {
        const restoreCaret = saveCaretPosition(this.firstChild);
        let htmlContent = $(this).html();
        catArray.forEach(cat => {
            const regex = new RegExp(`(\\b)(${cat})(\\b)(?!<\/span>)`, 'gi');
            htmlContent = htmlContent.replace(regex, `$1<span class='matchCat'>$2</span>$3`);
        });
        $(this).html(htmlContent);
        restoreCaret();
    }
});
© www.soinside.com 2019 - 2024. All rights reserved.