contenteditable去除html标签

2021-01-18 13:52:18

有时候我们实现了div的contenteditable属性,但是粘贴的时候有时候会携带html语句。我们需要去掉。

最近把Reader项目的评论框从textarea换成div了, 实现元素的自增高确实方便了, 但随之而来的却是更多的麻烦.

比如, div[contenteditable=true]元素的内容可以是HTML代码, 虽然直接输入不行, 但从网页上复制内容到编辑框却是可以的, 这带来了2个问题:

  1. 评论框的内容不统一, 排版混乱, 影响用户体验, 并且评论框本身期望的内容就是纯文字.

  2. 允许HTML代码提交会存在安全方面的问题.

原先的textarea在这方面就完全不用顾虑, 因为它可输入的内容就只有纯文字, 而且还支持input等表单元素特有的事件.

对于div[contenteditable=true], 我们需要用很多的代码将其实现成一个编辑器, 这对于快速开发的项目而言, 太不合适了, 那怎么办? 退回textarea吗? 若是退回textarea, 就无法轻松实现一些高级的功能, 而且这两种元素差别挺大, 并不方便切换.

所以有这样一种方法, 移除div[contenteditable=true]中的不安全HTML标签:


text = text.replace(/<[\/\s]*(?:(?!div|br)[^>]*)>/g,'')


这段代码会移除除了div和br以外的所有HTML标签, 但我们需要在用户输入时执行它, 所以免不了额外的计算消耗.

需要执行这段代码的事件有blur, paste, keyup, 而且在触发之前可能还需要设置一个timeout, 不然就无法获取到内容. 这些都不是实现功能的最大障碍, 最要命的是我们使用到了正则表达式, 这种东西极难维护, 后患无穷.

实际上, 上面代码给出的正则表达式还留有一个问题, 即无法去除div标签的属性.

所以我不得不再加上另外两行代码:

text = text.replace(/<[\/\s]*(?:(?!div|br)[^>]*)>/g, '')text = text.replace(/<\s*div[^>]*>/g, '<div>')text = text.replace(/<[\/\s]*div[^>]*>/g, '</div>')


也许这里还存在着一些其他的漏洞, 我只想告诉你, 这真的不是一个好的解决方案.

 

另一个解决方案是获取剪切板的纯文本内容, 然后将内容插入到编辑框中, 这样就避免了HTML代码的输入. 该代码只在现代浏览器中可以执行, 对于旧版本的浏览器, 可能需要另设兼容方案:

var clipboard = e.clipboardData,text = clipboard.getData('text/plain'),sel = window.getSelection();if(sel.getRangeAt && sel.rangeCount){var range = sel.getRangeAt(0),frag = document.createDocumentFragment(),node,lastNode;range.deleteContents();var ele = document.createElement("div");ele.innerHTML = text;while(node = el.firstChild){lastNode = frag.appendChild(node);}range.insertNode(frag);if(lastNode){range = range.cloneRange();range.setStartAfter(lastNode);range.collapse(true);sel.removeAllRanges();sel.addRange(range);}}


这段代码需要放在未调用settimeout的paste事件中(beforepaste), 否则将无法获得剪切板内容, 执行完毕后别忘了取消浏览器的默认事件.

这里用到了clipboardData, Selection, Range等W3C标准的对象, 虽然实现起来相比第一种方法要复杂一些, 但这换来了更轻松的代码维护工作.


  • 2018-08-07 20:00:42

    xUtils3.0版本的发送同步网络请求的方式

    对于Android开发来说,基本都是用异步来从网络上请求数据,很少用到同步请求的。近日项目有个地方需要使用到同步请求(以我目前的知识储备来说好像只能用同步请求来解决这个问题了),去网上搜索相关资料,又没有找到什么明确的使用方法。所以记下来,以备不时之需。

  • 2018-08-14 23:35:28

    Retrofit 设置 超时时间

    今天开发的时候遇到一个网络请求超时的问题,后台处理是成功的,但是移动端返回的总是提示请求超时,在设置了retrofit请求超时的时间延长以后,就可以请求成功了,下面是配置的方法:

  • 2018-08-16 16:10:43

    Laravel 跨域解决方案

    我们在用 laravel 进行开发的时候,特别是前后端完全分离的时候,由于前端项目运行在自己机器的指定端口(也可能是其他人的机器) , 例如 localhost:8000 , 而 laravel 程序又运行在另一个端口,这样就跨域了,而由于浏览器的同源策略,跨域请求是非法的。其实这个问题很好解决,只需要添加一个中间件就可以了。

  • 2018-08-18 20:30:12

    laravel5.5 路由分割成不同文件

    routes.php/api.php文件用来放置laravel路由,当项目越来越大,相应的路由文件也会越来越多。如果能够将不同功能的路由分割到不同的文件,那么对以后的维护将很有帮助。

  • 2018-08-20 15:26:19

    关于OnTouch 和OnClick同时调用冲突的解决方案

    大家在搞轮播图的时候会碰到这样的情况,点击进入webview界面,长按轮播图停止轮播,手松开图又开始轮播,这里就涉及到了OnTouch 和OnClick同时调用。两者是有冲突的。这里简单介绍,给大家提供思路。

  • 2018-08-20 15:29:11

    揭开RecyclerView的神秘面纱(二):处理RecyclerView的点击事件

    主要讲述了RecyclerView的基本使用方法,不同的布局管理器而造成的多样化展示方式,展示了数据之后,一般都会与用户进行交互,因此我们需要处理用户的点击事件。在ListView和GridView提供了onItemClickListener这个监听器,然而我们查找RecyclerView的API却没有类似的监听器,因此我们需要自己手动处理它的点击事件。 以下提供两种方法来实现处理RecyclerView点击事件的功能,以下代码均基于上一篇文章的代码做出修改。

  • 2018-08-20 22:58:46

    onInterceptTouchEvent和onTouchEvent调用关系详解 ...

    老实说,这两个小东东实在是太麻烦了,很不好懂,我自己那api文档都头晕,在网上找到很多资料,才知道是怎么回事,这里总结一下,记住这个原则就会很清楚了:

  • 2018-08-23 15:32:18

    map对象拷贝问题

    最后面是使用序列化的方式,发现,更改引用类型的数据的时候,mapNew对象并没有发生变化,所以产生了深拷贝。 上述的工具类,可以实现对象的深拷贝,不仅限于HashMap,前提是实现了Serlizeable接口。