动态发布 富文本中@与#变色与删除

1,544 阅读6分钟

笔者在做发布页的时候,需要使用到html的富文本

需求是:
1:当用户输入到@符号的时候,跳转到@列表,点击@列表返回发布页面文本添加@人名。@人名也要变成一个指定的颜色,与其他文本进行区分。

2:当用户删除@人名的时候,@人名被整体删除。

首先回顾下节点的类型

   nodeType的类型
   1:Element节点 
   3:Text节点
   8:Comment节点
   9:Document节点

第一步 创建富文本并监听输入事件

首先创建一个富文本的节点

    <div id="textarea" contenteditable="true"></div>

然后就是监听文本的每次输入

    document.getElementById('textarea').oninput = function (event) {console.log(event.data)}

你就可以发现每次输入,打印输入的文本内容,删除打印的是null

下面是上述完整的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.0">    
        <title>Document</title></head><body>
            <div id="textarea" contenteditable="true"></div>
        <script>
             document.getElementById('textarea').oninput = function (event) { console.log(event.data) }
       </script></body> </html>

第二步 创建一个@名字的按钮,来模拟@名字,从@页面返回的输入

创建按钮节点

   <button id="btn">add@名字</button>

添加按钮事件

//添加指定颜色的文本    //@ #专用    
function addHtmlDoc(text) {        
    et str = document.getElementById('textarea').innerHTML
    document.getElementById('textarea').innerHTML = str + text;    
 }
//按钮事件
document.getElementById('btn').addEventListener('click', () => {
   addHtmlDoc('<font color="#0000ff">@名字</font>'); //添加的文本内
})

下面是上述完整的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.0">    <title>Document</title></head><body>    <div id="textarea" contenteditable="true"></div>    <button id="btn">add@名字</button>    <script>        function addHtmlDoc(text) {            let str = document.getElementById('textarea').innerHTML            document.getElementById('textarea').innerHTML = str + text;        }        document.getElementById('textarea').oninput = function (event) { console.log(event.data) }        document.getElementById('btn').addEventListener('click', () => {            addHtmlDoc('<font color="#0000ff">@名字</font>')        })    </script></body></html>

但是这里面有问题你会发现,继续输入文本,后面输入文本颜色尽然就是前面@名字的颜色,你按下F12观察富文本的节点,你会发现输入的文本会被放在

<font color="#0000ff">@名字</font>

的节点中,如下图

不太符合需求,我输入的1231文本被放入了之前的@节点中了,那要怎么办呢。

方法一:监听输入的文本,例如输入文本进行处理,让输入的文本放在#textarea(富文本)的节点一级子节点。

方法二:既然输入的文本延续了前一个文本的属性那么我们干脆在输入完@名字之后。后面追加一个空格,空格放在@名字节点之外,也就是富文本的一级子节点

//添加指定颜色的文本    //@ #专用    
function addThenColorDoc(text,color ) {        
    addHtmlDoc(`<font trg='@' color=${color}>${text}</font>&nbsp;`);    
}

同时将@名字按钮里的方法进行重新处理

    document.getElementById('btn').addEventListener('click', () => {
         addThenColorDoc("#0000ff", '@名字')
     })

同时我们在@名字后面输入文本。发现成功了

下面是上述完整的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.0">    <title>Document</title></head><body>    <div id="textarea" contenteditable="true"></div>    <button id="btn">add@名字</button>    <script>        function addHtmlDoc(text) {            let str = document.getElementById('textarea').innerHTML            document.getElementById('textarea').innerHTML = str + text;        }        //添加指定颜色的文本        //@ #专用        function addThenColorDoc(text, color = '#0000ff') {            addHtmlDoc(`<font trg='@' color=${color}>${text}</font>&nbsp;`);        }        document.getElementById('textarea').oninput = function (event) { console.log(event.data) }        document.getElementById('btn').addEventListener('click', () => {            addThenColorDoc('@名字', "#0000ff")        })    </script></body></html>

第三步 选区

我们想要编辑文本,那就必须要知道并能够获取特定的文本

在富文本中有个选区的api,能将鼠标按下后移动松后开,被蓝色的背景选中的文本节点变为选区。

//我们可以通过这个方法或当前我们上述选中的文本。
window.getSelection();

我们加入一个按钮跟它的事件就是测试能不能将鼠标选中的文本改变颜色

   <button id="btn2">将选中的文本修改颜色</button>

   //将选中的文本修改颜色        
    document.getElementById('btn2').addEventListener('click', () => {
            document.execCommand('forecolor', false, 'blue');       
    }) 

我选中之后 点击按钮(将选中的文本修改颜色)

上述的就是选区之后,使用代码控制变色的表现。

下面是上述完整的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.0">    <title>Document</title></head><body>    <div id="textarea" contenteditable="true"></div>    <button id="btn">add@名字</button>    <button id="btn2">将选中的文本修改颜色</button>    <script>        function addHtmlDoc(text) {            let str = document.getElementById('textarea').innerHTML            document.getElementById('textarea').innerHTML = str + text;        }        //添加指定颜色的文本        //@ #专用        function addThenColorDoc(text, color = '#0000ff') {            addHtmlDoc(`<font trg='@' color=${color}>${text}</font>&nbsp;`);        }        //监听文本的输入        document.getElementById('textarea').oninput = function (event) { console.log(event.data) }        document.getElementById('btn').addEventListener('click', () => {            addThenColorDoc('@名字', "#0000ff")        })        //将选中的文本修改颜色        document.getElementById('btn2').addEventListener('click', () => {            document.execCommand('forecolor', false, 'blue');        })     </script></body></html>

当然在选中的有很多可以使用的api

你可以点进去详细的查看 developer.mozilla.org/zh-CN/docs/…

第四步 通过删除事件获取到要删除的文本选区

我们当删除的话,富文本监听返回的是event.data为null

这样我们就能取到删除事件了

我们把事件监听进行修改为

//删除指定字符
function deleteText() {      
    let Selection = window.getSelection();
    console.log('删除时的选区:', Selection);
}
//监听文本的输入       
document.getElementById('textarea').oninput = function (event) {
    console.log(event.data)
    //删除文本的时候      
    if (!event.data) { 
     deleteText(); 
    } 
}

下面是上述完成的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.0">    <title>Document</title></head><body>    <div id="textarea" contenteditable="true"></div>    <button id="btn">add@名字</button>    <button id="btn2">将选中的文本修改颜色</button>    <script>        function addHtmlDoc(text) {            let str = document.getElementById('textarea').innerHTML            document.getElementById('textarea').innerHTML = str + text;        }        //添加指定颜色的文本        //@ #专用        function addThenColorDoc(text, color = '#0000ff') {            addHtmlDoc(`<font trg='@' color=${color}>${text}</font>&nbsp;`);        }        //删除指定字符        function deleteText() {            let Selection = window.getSelection();            console.log('删除时的选区:', Selection)        }        //监听文本的输入        document.getElementById('textarea').oninput = function (event) {            console.log(event.data)            //删除文本的时候            if (!event.data) {                deleteText();            }        }        document.getElementById('btn').addEventListener('click', () => {            addThenColorDoc('@名字', "#0000ff")        })        //将选中的文本修改颜色        document.getElementById('btn2').addEventListener('click', () => {            document.execCommand('forecolor', false, 'blue');        })    </script></body></html>

把他复制下来,输入文本然后删除一个文本观察打印的数据

我点击了 add@名字 的按钮 然后删除了它后面的一个空格,下面是控制台打印出来的数据。

当我把鼠标放在红色框框上的时候,发现左边竟然被选中了。

说明我们删除出文本的时候,会自动选中当前删除的文本选区。这样我们就知道当前要删除节点(知道节点就能知道文本)。我们只需要将判断当前的选中是不是我们要移除的@名字的节点就可以了

第四步 删除发生时选区是不是要删除的节点(@名字)

当我们拿到要删除的选区。我们就能通过一些判断来断定当前的选区是不是要删除的节点。

也就是开头提到的节点类型,当前我们只会接触到element节点跟文本节点

我们继续看打印出来的anchorNode里面有什么可以使用的内容。

发现我们能取到当前的 nodeType(节点类型) nodeValue(文本内容) parentElement(父节点)

在经过观察发现父节点就是

我们想要删除的element节点! 

判断文本中选在@字段并校验@后面的名字就是我们要删除的文本,直接调取element.remove()不就完成了嘛!我要去试试,

将删除方法进行修改

//删除指定字符        
function deleteText() {
    let Selection = window.getSelection();
    let anchorNode = Selection.anchorNode;
    console.log('删除时的选区:', Selection)
    if (/@/.test(anchorNode.data)) {
        //所以就需要提案加一些跳转来判断是不是要要删除的节点 例如判断[@文本]文本中是不是你需要被删除的数据 或者判断这个节点的属性是不是存在你自己定义的一些标识                
        anchorNode.remove();
    } 
}

测试发现@名字确实能够被一键删除

下面是上述完整的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.0">    <title>Document</title></head><body>    <div id="textarea" contenteditable="true"></div>    <button id="btn">add@名字</button>    <button id="btn2">将选中的文本修改颜色</button>    <script>        function addHtmlDoc(text) {            let str = document.getElementById('textarea').innerHTML            document.getElementById('textarea').innerHTML = str + text;        }        //添加指定颜色的文本        //@ #专用        function addThenColorDoc(text, color = '#0000ff') {            addHtmlDoc(`<font trg='@' color=${color}>${text}</font>&nbsp;`);        }        //删除指定字符        function deleteText() {            let Selection = window.getSelection();            let anchorNode = Selection.anchorNode;            console.log('删除时的选区:', Selection)            if (/@/.test(anchorNode.data)) {                //所以就需要提案加一些跳转来判断是不是要要删除的节点 例如判断[@文本]文本中是不是你需要被删除的数据 或者判断这个节点的属性是不是存在你自己定义的一些标识                anchorNode.remove();            }        }        //监听文本的输入        document.getElementById('textarea').oninput = function (event) {            console.log(event.data)            //删除文本的时候            if (!event.data) {                deleteText();            }        }        document.getElementById('btn').addEventListener('click', () => {            addThenColorDoc('@名字', "#0000ff")        })        //将选中的文本修改颜色        document.getElementById('btn2').addEventListener('click', () => {            document.execCommand('forecolor', false, 'blue');        })    </script></body></html>

第五步 返回看问题

但是出现了一个问题,用户手动输入@名字 与 点击按钮生成的带颜色的@名字 都会被一键删除,你可以是在第四步完整的html文档中复制文本,然后运行html文件 复制粘贴如下文本

<div id="textarea" contenteditable="true">123123<font trg="@" color="#0000ff">@名字</font>&nbsp;123123@名字<font trg="@" color="#0000ff"></font></div>

“0@名字”   也一并被删除了。但是0@名字并未被包含在之间。说明判断的条件有问题。

具体怎么处理需要你根据自己的实际业务进行添加不同的条件

我初步的方法是给

@名字

我初步的写法是给中添加上一个trg='@' 然后删除文本的时候,不仅仅判断文本与@,

同时判断当前选中中是不是存在trg='@'的属性

修改删除文本中判断的方法

 //删除指定字符        
function deleteText() {            
    let Selection = window.getSelection();
    let anchorNode = Selection.anchorNode; 
    console.log('删除时的选区:', Selection)
    if (/@/.test(anchorNode.data)) {
    //所以就需要提案加一些跳转来判断是不是要要删除的节点 例如判断[@文本]文本中是不是你需要被删除的数据 或者判断这个节点的属性是不是存在你自己定义的一些标识                
    let node = anchorNode.parentElement;//得到的就是文本的node父节点 也就是`<font trg='@' color=${color}>${text}</font>&nbsp;这个节点<font>`                
    let attributes = node.attributes               
    if (attributes.trg && attributes.trg.nodeValue) {  
        if (attributes.trg.nodeValue === '@') {    
                anchorNode.remove();
            } 
        }
    } 
}

好啦,当前的问题就处理完毕了,谢谢大家