简体   繁体   English

如何在跨度中将输入的文本包装在可内容编辑的元素中?

[英]How can I wrap inputted text in a contenteditable elememt in a span?

I have a div that has "contenteditable" set to true. 我有一个将“ contenteditable”设置为true的div。

I now want any newly inputted text to be wrapped in a span. 现在,我希望将任何新输入的文本包装在一个跨度中。 Meaning I can highlight the edited text. 意思是我可以突出显示已编辑的文本。

Here is where I'm at at the moment - http://jsfiddle.net/ug4xr9x0/ 这是我目前的位置-http://jsfiddle.net/ug4xr9x0/

HTML HTML

<div contenteditable="true" id="myContent">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi suscipit tincidunt leo, non volutpat lectus sodales non. Phasellus id felis ultrices, egestas enim nec, ornare quam. Ut at posuere felis, vitae sodales risus. Suspendisse velit nibh, facilisis et ante et, vehicula facilisis velit.</div>

Javascript Java脚本

document.getElementById('myContent').addEventListener('keydown',function(evt){
    var range = window.getSelection().getRangeAt(0);
    if(range.startContainer.parentNode.className!=='highlight'){
        var newElement = document.createElement('span');
        newElement.className = 'highlight';
        newElement.innerHTML = String.fromCharCode(evt.which);
        range.insertNode(newElement);
        range.setStartAfter(newElement);
        evt.preventDefault();
    }
});

CSS CSS

.highlight{
    background-color:yellow;
}

This wraps the span around the character but it doesn't move the caret into the span, resulting in backwards typing. 这将跨度环绕在字符周围,但不会将插入号移动到跨度中,从而导致向后键入。 I'm not at all certain that keydown is the appropriate event to listen to, or even that blocking the keypress and inserting an element is correct. 我完全不确定keydown是监听的适当事件,甚至不确定阻止keypress和插入元素是否正确。 The line range.setStartAfter was an attempt to move the caret along, but it behaves the same with or without it. 范围range.setStartAfter是试图移动插入号的尝试,但无论有没有,其行为都相同。

I also want any selected text to be deleted when they start typing which isn't happening at the moment. 我还希望所有选定的文本在开始键入时都被删除,而这目前还没有发生。

Any help would be much appreciated. 任何帮助将非常感激。 Thanks. 谢谢。

onkeydown is suitable for this, though oninput would be even better, but it doesn't fire on contenteditables in all browsers. onkeydown适用于此,尽管oninput会更好,但是并不能在所有浏览器中的contenteditables上触发。 Also when you want to add an element, inserNode is an apppropriate method to do it. 同样,当您要添加元素时, inserNode是执行此操作的适当方法。 setStartAfter is just for the task you've used it, though it can be done with range.collapse() as well. 尽管也可以使用range.collapse()来完成, setStartAfter仅用于您使用过的任务。

Problems start when setting content to the newly-created span with newElement.innerHTML = String.fromCharCode(evt.which); 当使用newElement.innerHTML = String.fromCharCode(evt.which);将内容设置为新创建的span时,问题就开始了newElement.innerHTML = String.fromCharCode(evt.which); . The inserted text isn't necessarily what user has intended to enter. 插入的文本不一定是用户想要输入的内容。

This snippet is just slightly modified from your code, but it shows the text user has entered. 该代码段仅在您的代码中稍作修改,但显示了用户输入的文本。

document.getElementById('myContent').addEventListener('keydown', function (evt) {
    var range = window.getSelection().getRangeAt(0),
        modifiers = [8, 13, 37, 38, 39, 40, 46], // List of keys, which should not prevent the default action
        newElement, chr;
    if (range.startContainer.parentNode.className !== 'highlight') {
        if (modifiers.indexOf(evt.which) < 0) {
            newElement = document.createElement('span');
            newElement.className = 'highlight';
            range.deleteContents(); // This deletes the content of the selection
            chr = evt.key || evt.char; // Gets the entered real character
            if (chr.length < 2) { // Excludes "modifiers" like SHIFT and CTRL etc.
                newElement.textContent = chr;
            }
            range.insertNode(newElement);
            range.collapse(); // Collapses the range to end
            evt.preventDefault();
        }
    }
});

A live demo at jsFiddle . jsFiddle上的现场演示

There seems to be a drawback in this code. 这段代码似乎有一个缺点。 Every entered character creates a new span , unless you really write to a span . 除非您真正写入span ,否则每个输入的字符都会创建一个新的span

I've managed to get this going in Chrome and Firefox now. 现在,我已经设法在Chrome和Firefox中进行此操作。 Here are the modifications 这是修改

document.getElementById('myContent').addEventListener('keypress',function(evt){
    var range = window.getSelection().getRangeAt(0),
        modifiers = [0,8]; // List of keys to ignore (0 is arrows & 8 is delete in firefox)

    if(range.startContainer.parentNode.className!=='highlight' && modifiers.indexOf(evt.which) <  0){
        if(!range.collapsed){
            range.deleteContents();
        }
        var el =  document.createElement('span');
        el.appendChild(document.createTextNode('A'));
        el.className = 'highlight';
        range.insertNode(el);

        var sel = window.getSelection();        
        range.setStartBefore(el.childNodes[0]);
        range.setEndAfter(el.childNodes[0]);
        sel.removeAllRanges();
        sel.addRange(range);
    }
});

And the fiddle http://jsfiddle.net/ug4xr9x0/8/ 和小提琴http://jsfiddle.net/ug4xr9x0/8/

"keypress", as opposed to "keydown", stops the arrow keys etc firing in chrome, firefox needed to ignore the "evt.which" property at 0 & 8. “ keypress”(而不是“ keydown”)可停止箭头键等在chrome中触发,firefox需要忽略0和8处的“ evt.which”属性。

Is inserts a span with a textnode containing the letter 'A'. 将插入一个跨度,该跨度的textnode包含字母“ A”。 I then set the range to wrap that node using setStartBefore & setEndAfter and update the window selection using addRange. 然后,我使用setStartBefore和setEndAfter设置范围以包装该节点,并使用addRange更新窗口选择。 The textnode 'A' gets overwritten (no need for prevent default). 文本节点“ A”被覆盖(无需阻止默认值)。

I'm sure there is a more elegant way to achieve this, but it works for me. 我敢肯定,有一种更优雅的方法可以实现这一点,但是它对我有用。

Thanks for your help. 谢谢你的帮助。

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

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