zoukankan      html  css  js  c++  java
  • 如何让contenteditable元素只能输入纯文本

    本文出自张旭鑫博客,要知详情,请戳右侧地址:http://www.zhangxinxu.com/wordpress/?p=5120

    一、温故而知新

    很多年以前,稍等,让我搜一下contenteditable(右上角),哈,是2010年的时候,写了篇文章“div模拟textarea文本域轻松实现高度自适应”,就是说的contenteditable的应用。

    虽然说,利用全浏览器都支持的contenteditable模拟文本域可以实现体验相当不错的高度跟随内容自动撑开的效果,但是呢,有个很大的问题就是HTML内容可以直接被粘贴进去,如下图所示:

    复制的HTML代码显示 张鑫旭-鑫空间-鑫生活

    之前的文章提到过过滤HTML的方法,保证内容都是纯文本。然而,这种方法的问题在于:

    1. 粘贴完毕到过滤结束有时间差,用户很看到内容一闪而过的糟糕体验;
    2. 光标的位置会发生变化,不是之前focus的位置了;

    当年的我图样图森破,所以,只有上面这种程度。实际上,控制contenteditable元素只能输入纯文本是有体验比较好的方法的。

    二、与contenteditable属性无关的CSS控制法

    一个div元素,要让其可编辑,也就是可读写,contenteditable属性是最常用方法,做前端的基本上都知道。但是,知道CSS中有属性可以让普通元素可读写的的同学怕是就少多了。

    主角亮相:user-modify.

    支持属性值如下:

    user-modify: read-only;
    user-modify: read-write;
    user-modify: write-only;
    user-modify: read-write-plaintext-only;
    

    其中,write-only不用在意,当下这个年代,基本上没有浏览器支持,以后估计也不会有。read-only表示只读,就是普通元素的默认状态啦。然后,read-writeread-write-plaintext-only会让元素表现得像个文本域一样,可以focus以及输入内容。

    您可以狠狠地点击这里:CSS user-modify属性行为表现demo

    会发现,设置了read-writeread-write-plaintext-only值的两个<p>标签元素是可以被focus的:
    focus时候的状态截图

    而这两者的区别就在于,一个可以输入富文本,而下面一个只能输入纯文本,例如,我们从某网页同时复制一段内容粘贴进去看看:
    富文本和纯文本使用示意截图

    好了,至此,本文标题的答案实际上就已经有了。也就是给元素设置:

    user-modify: read-write-plaintext-only

    就可以让元素既可以编辑,也只能输入纯文本,表现得就跟textarea文本域一样。

    是不是很酷啊!然而,抱歉地跟大家讲下,目前只有webkit内核浏览器才支持read-write-plaintext-only这个值,因此,我们的使用其实是:

    -webkit-user-modify: read-write-plaintext-only

    我们可以在移动端使用,以及,只需要兼顾webkit内容的桌面网页项目。

    三、使用标准contenteditable属性值的HTML控制法

    咳咳,提问:在HTML中,contenteditable支持的属性值是?

    图样图森破时候的我,脑中就只有contenteditable="true"contenteditable="false",科科,后来我发现自己太天真了,新的草案中明确表示还有多个其他属性值:

    The contenteditable attribute is an enumerated attribute whose keywords are the empty string (“”), “events”, “caret”, “typing”, “plaintext-only”, “true”, and “false”. There is one additional state, the inherit state, which is the missing value default (and the invalid value default).

    垂直展示下就是(不包括默认的inherit继承):

    contenteditable=""
    contenteditable="events"
    contenteditable="caret"
    contenteditable="plaintext-only"
    contenteditable="true"
    contenteditable="false"

    别问我,我也不知道"events""caret"是干什么用的,嘿,但是"plaintext-only"我是知道的,可以让编辑区域只能键入纯文本

    <div contenteditable="plaintext-only"></div>
     

    如果您发现,居然出乎意料,可以弄进去富文本,那说明你使用的是非Chrome之流的浏览器。

    换句话说,contenteditable="plaintext-only"和CSS只的-webkit-user-modify: read-write-plaintext-only一样,目前仅仅是Chrome浏览器支持比较好的。

    所以,您的项目如果还有很多IE8浏览器的用户,我只能替你惋惜,美妙的东西无法立即用上,不得已,寻求下面的方法。

    四、控制粘贴paste事件的JS控制法

    如果我们单纯地敲击键盘,输入的内容实际上都是纯文本。除了一些特殊情况,例如IE浏览器下的编辑框会自动把合乎条件的url地址自动加上链接。富文本污染的情况主要出现在复制粘贴的时候,于是,如果我们能在粘贴的时候,对剪切板中的内容进行HTML过滤,再手动插入内容,岂不就可以完美解决无法输入富文本的问题了吗!?

    于是,鄙人不才,一番折腾,弄出了下面的代码:

    $('[contenteditable]').each(function() {
        // 干掉IE http之类地址自动加链接
        try {
            document.execCommand("AutoUrlDetect", false, false);
        } catch (e) {}
        
        $(this).on('paste', function(e) {
            e.preventDefault();
            var text = null;
        
            if(window.clipboardData && clipboardData.setData) {
                // IE
                text = window.clipboardData.getData('text');
            } else {
                text = (e.originalEvent || e).clipboardData.getData('text/plain') || prompt('在这里输入文本');
            }
            if (document.body.createTextRange) {    
                if (document.selection) {
                    textRange = document.selection.createRange();
                } else if (window.getSelection) {
                    sel = window.getSelection();
                    var range = sel.getRangeAt(0);
                    
                    // 创建临时元素,使得TextRange可以移动到正确的位置
                    var tempEl = document.createElement("span");
                    tempEl.innerHTML = "&#FEFF;";
                    range.deleteContents();
                    range.insertNode(tempEl);
                    textRange = document.body.createTextRange();
                    textRange.moveToElementText(tempEl);
                    tempEl.parentNode.removeChild(tempEl);
                }
                textRange.text = text;
                textRange.collapse(false);
                textRange.select();
            } else {
                // Chrome之类浏览器
                document.execCommand("insertText", false, text);
            }
        });
    });

    兴趣使然,目前还没再真实项目中实践过,因此,可能还有瑕疵或者缺陷。自己在demo上,IE8+,Chrome等浏览器都测试过,都是可以的。对了,demo要先放出来。

    您可以狠狠地点击这里:contenteditable元素纯文本输入控制demo

    demo页面有个框框,大家可以试试看,是不是只能弄进去纯文本。

    demo界面样子截图

    关于代码的一些说明

    • 一开始的$('[contenteditable]').each()只是示意,,里面的核心代码与jQuery没有任何关系,大家可以灵活介入自己项目。
    • IE浏览器的contenteditable框有个问题,会自动添加链接,我们需要的是纯文本,显然这种自以为是的行为不是我们要的,可以使用document.execCommand("AutoUrlDetect", false, false)来进行处理。
    • 理想情况应该直接使用document.execCommand("insertText")命令插入内容。但是,但是,IE浏览器虽然运行这段代码没有出错,也是支持document.execCommand的,但是,却没有插入内容的表现。也不知道是不是我打开的方式不对,后来,我就寻求更传统的方法,创建文本选区与插入,正好,就IE支持document.body.createTextRange
    • document.selectionIE浏览器一直是支持的,直到IE11浏览器,直接废弃了,好在window.getSelection还活着,于是,又一次分情况处理。
      是否支持document.selection
    • 获得剪切板数据,不同浏览器情况也不一样,这里不赘述了,因为已经1点多了,年纪大了,实在熬不住了……
    • 兼容性甩CSS方法和HTML方法两条街,我自己使用的浏览器都测过没问题,当然,demo比较简单,测试可能不能说明全部问题。
  • 相关阅读:
    2018 ACM 网络选拔赛 徐州赛区
    2018 ACM 网络选拔赛 焦作赛区
    2018 ACM 网络选拔赛 沈阳赛区
    poj 2289 网络流 and 二分查找
    poj 2446 二分图最大匹配
    poj 1469 二分图最大匹配
    poj 3249 拓扑排序 and 动态规划
    poj 3687 拓扑排序
    poj 2585 拓扑排序
    poj 1094 拓扑排序
  • 原文地址:https://www.cnblogs.com/sheshou/p/5416885.html
Copyright © 2011-2022 走看看