前言

  最近在弄个人的网站,偶然间发现DIV可以设置编辑模式,之前设计的方案在此功能上需要限制输入的长度。网上搜索了一波,综合搜索的结果,考虑使用的监听事件有:keydown 、textInput 、input。因为可能输入中文,所以也要监听compositionstart和compositionend两个事件。

  在试用过程中,发现:

  1. Keydown是监控到键盘的输入出发,调用顺序在compositionstart和compositionend之间,导致无法对中文的输入进行实时的监控。
  2. textInput和keydown一样,对中文一样无力,而且无法对粘贴进行监控。
  3. Input能对所有value改变事件进行监控,但是不支持撤销功能。

  不用想,肯定选择范围最宽的'Input'了,没有撤销?那就实现一个。

思路及实现

  撤销在文本框的直接感觉回到操作之前的文本(Ps:这里只考虑字符串的增加操作)。那将上一步添加的数据删除,已达到撤销相同的效果不就可以了。那么首先要得到上一步操作时光标位置的初始位置以及光标的偏移量,然后对操作后的字符串中添加的字串移除不就可以了。 顺着这个思路得到下面的代码。

 var content = document.getElementById('add-content')
//注册中文的输入事件,
var isCN = false;
content.addEventListener('compositionstart',function(){
  isCN = true;
});
content.addEventListener('compositionend',function(){
  isCN = false;
})
//注册文本输入事件,获取光标的起止偏移数据,如果是非中文以及超出长度的输入,则撤销本次操作
var txtAnchorOffset, txtFocusOffset;
content.addEventListener("textInput",function(event){
  var _sel = document.getSelection();
  txtAnchorOffset = _sel.anchorOffset;
  txtFocusOffset = _sel.focusOffset;
  //必须加上isCN的判断,否则获取不到正确的光标数据
  if(!isCN && this.textContent.length >= noteMax){
    event.preventDefault();
  }
}); //注册输入事件,对输入的数据进行
content.addEventListener("input",function(event){
  setTimeout(function(){
    if(!isCN){
      var _this = content;
      if(_this.textContent.length > noteMax){
        var data = _this.textContent;
        oldDate = data.slice(, txtAnchorOffset) + data.slice(txtFocusOffset, data.length);
        //再次截取最大长度字符串,防止溢出
        _this.textContent = oldDate.slice(, noteMax);
      }
    }
  }, );
});

  Emmmm,的确能移除了,但是光标却回到了字符串的开始位置。这可不行,这样不就成了阉割版的了吗? 继续查询网上移动文本域光标的实现方法,move()、setSelectionRange()在运行的时候都提示” xx is not a funtion”,网上查询后,好像说是只支持input和textarea标签。

  后面在MDN官方文档上看到这个函数:

    Selection.collapse();  参数:parentNode光标落在的目标节点 offset 落在节点的偏移量。

  有了函数,那就来开干!!

 //注册输入事件,对输入的数据进行
content.addEventListener("input",function(event){
  setTimeout(function(){
    if(!isCN){
      var _this = content;
      if(_this.textContent.length > noteMax){
        var data = _this.textContent;
        oldDate = data.slice(, txtAnchorOffset) + data.slice(txtFocusOffset, data.length);
        //再次截取最大长度字符串,防止溢出
        _this.textContent = oldDate.slice(, noteMax);
        //光标移动到起始偏移位置
        document.getSelection().collapse(_this.firstChild, txtAnchorOffset);
      }
    }
  }, );
});

  注意:parentNode参数选择文本节点。

  开始的时候,我传的值是_this,提示“Failed to execute 'collapse' on 'Selection': There is no child at offset X.”,后面,在debugger中发现文本域是div对象的子节点,有想法就去尝试。改成_this.firstChild后,目标达成。

  到此基本上已经完成功能的设计,然后在加上移除粘贴内容就行了。完成的代码如下:

html

<div id="add-content" contenteditable="true" ></div>

Javascript

 var content = document.getElementById('add-content')
//注册中文的输入事件,
var isCN = false;
content.addEventListener('compositionstart',function(){
isCN = true;
//撤销预输入内容,必须否则会替代末尾字符
if(this.textContent.length >= noteMax){
event.preventDefault();
}
});
content.addEventListener('compositionend',function(){
isCN = false;
})
//注册文本输入事件,获取光标的起止偏移数据,如果是非中文以及超出长度的输入,则撤销本次操作
var txtAnchorOffset, txtFocusOffset;
content.addEventListener("textInput",function(event){
var _sel = document.getSelection();
txtAnchorOffset = _sel.anchorOffset;
txtFocusOffset = _sel.focusOffset;
//必须加上isCN的判断,否则获取不到正确的光标数据
if(!isCN && this.textContent.length >= noteMax){
event.preventDefault();
}
});
//注册粘贴事件,获取粘贴数据的长度
var pastedLength;
content.addEventListener("paste",function(event){
if(!event.clipboardData) return;
pastedLength = event.clipboardData.getData('Text').length;
}); //注册输入事件,对输入的数据进行
content.addEventListener("input",function(event){
setTimeout(function(){
if(!isCN){
var _this = content;
if(_this.textContent.length > noteMax){
var data = _this.textContent;
if(pastedLength > ){
oldDate = data.slice(, txtAnchorOffset) + data.slice(txtFocusOffset+pastedLength, data.length);
//粘贴字符串长度置为0,以免影响到下一次判断。
pastedLength = ;
} else {
oldDate = data.slice(, txtAnchorOffset) + data.slice(txtFocusOffset, data.length);
}
//再次截取最大长度字符串,防止溢出
_this.textContent = oldDate.slice(, noteMax);
//光标移动到起始偏移位置
document.getSelection().collapse(_this.firstChild, txtAnchorOffset); }
}
}, );
});

总结

  在这次的编码中,其实试过很多个方法,尤其是光标那一部分,失败了很多次。然后继续搜索寻找方法。最后还是在官方文档里面找到突破点,网上很多经验都是别人消化并根据自己的理解写出来的,官方文档却是原滋原味的,所以一定要以官方文档为参照,不要迷失方向。 中间其实曾放弃了一次,将div改成textarea光标一下子就能正确偏移了。但是想了一下,还是重新改成div,继续出发,继续寻找正确的方向,最后终于完成了想要的功能。

  有想法就去实现它!!!成功了,能收获喜悦;不成功,也能在途中拓宽自己的知识面。还有,最好有个地方将其记录并分享出来,不管是成功或失败。成功的经验,或许能帮助到他人;失败的记录,或许能获得他人的帮助,然后再去完成它。

DIV可编辑后,与限制输入及光标偏移的纠葛的更多相关文章

  1. 2python脚本在window编辑后linux不能执行的问题

    参考简书博主天道酬勤abcd python脚本在windows编辑后,在linux下执行提示 /usr/bin/python^M: bad interpreter: No such file or d ...

  2. 子div设置float后导致父div无法自动撑开的问题

    子div设置float后会导致父div无法自动撑开 原因:内部的DIV因为float:left之后,就丢失了clear:both和display:block的样式,所以外部的DIV不会被撑开. 以下是 ...

  3. 百度编辑器ueditor每次编辑后多一个空行的解决办法

    用ueditor进行编辑文章时,每次编辑后文章前面都会多出一个空行. <script id="editor" type="text/plain" styl ...

  4. WPF 自带Datagrid编辑后无法更新数据源的问题

    原文  WPF 自带Datagrid编辑后无法更新数据源的问题 解决办法: 在列的绑定属性里加上UpdateSourceTrigger,示例XAML如下 <DataGrid Grid.Row=& ...

  5. elementUi中input输入字符光标在输入一个字符后,光标失去焦点

    elementUi中input输入字符光标在输入一个字符后,光标就退出,无法输入需要再次聚焦然后输入一个字符又再次退出 首先,用elementUi正常用v-model绑定输入的值是不会造成光标退出的, ...

  6. 编辑后保留原URl搜索条件

    首先需要知道的一个知识点: 1.request.GET是一个QueryDict类型的,要想取出?后面的结构就用request.GET.urlencode() 2.request.GET默认是不可修改的 ...

  7. 在.txt文件的首行写上.LOG后,后面每次对改文本文件进行编辑后,系统会自动在编辑内容后记录操作时间

    在.txt文件的首行写上.LOG后,后面每次对改文本文件进行编辑后,系统会自动在编辑内容后记录操作时间

  8. PyCharm编辑HTML文件时输入{%不能自动补全

    在PyCharm编辑HTML文件时输入Django模板语言时,发现录入 {% 不能自动补全. 找了一下,发现 setting 里可以设置 Python Template Languages,选择自己使 ...

  9. appium-清空输入框的内容后,再次输入内容会回退最后两个字符串

    问题描述 有两个输入框,用户名和密码输入框 调用set_text方法,输入用户名 再次调用set_text方法,输入密码 清空用户名输入框的内容后,再次输入内容会回退最后两个字符串 出问题的代码 de ...

随机推荐

  1. zoj3777(状态压缩)

    题目阐述: 给定n个座位,n个人,每个人可以做n个位置中的任意一个,P[i][j]代表第i个人做第j个位置获得的分数,求有多少种排列方式使得获得的分数大于等于M. 这道题跟数位dp的思想很像,都是穷举 ...

  2. 开源ORM

    Farseer.net轻量级开源框架 目录 https://github.com/steden/Farseer.net ---------------------------------- SunSo ...

  3. J20170524-hm

    取りこぼし 意外地输给较自己实力弱的对手,爆出冷门,败给手下败将 振り分け 分配,整理 スキーマ 图解.模式.图式

  4. python 闭包 Closure 函数作为返回值

    一.函数作为返回值 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. >>> def lazy_sum(*args): ... def sum(): ... ax = ...

  5. bzoj 5195: [Usaco2018 Feb]Directory Traversal【树形dp】

    注意到目录是一颗树结构,然后就简单了,预以1为根的处理出dis[u]为以这个点为根,到子树内的目录总长,si为子树内叶子数 第二遍dfs换根即可 #include<iostream> #i ...

  6. bzoj 4540: [Hnoi2016]序列【单调栈+线段树】

    强烈安利:http://blog.csdn.net/qq_34637390/article/details/51313126 这篇讲标记讲的非常好,这个标记非常神奇-- 首先last表示扫描到last ...

  7. 转载:4412环境搭建:arm-linux-gcc: 没有那个文件或目录

    4412环境搭建:arm-linux-gcc: 没有那个文件或目录 2014年10月15日 ⁄ 环境搭建 ⁄ 共 993字 ⁄ 字号 小 中 大 ⁄ 评论 11 条 ⁄ 阅读 6,125 次 最近弄了 ...

  8. 数据结构 - 顺序栈的实行(C语言)

    数据结构-顺序栈的实现 1 顺序栈的定义 既然栈是线性表的特例,那么栈的顺序存储其实也是线性表顺序存储的简化,我们简称为顺序栈.线性表是用数组来实现的,对于栈这种只能一头插入删除的线性表来说,用数组哪 ...

  9. [POI2007]石头花园SKA

    Description Blue Mary是一个有名的石头收藏家.迄今为止,他把他的藏品全部放在他的宫殿的地窖中.现在,他想将他的藏品陈列在他的花园中.皇家花园是一个边长为1000000000单位的平 ...

  10. ASP.NET MVC 实现伪静态

    1  什么是伪静态? 现在很多门户网站或者各大电商平台的网站的链接最后都是.htm或者.htm结尾,那么他们的网页真的是静态的html吗?拿京东来说,有无数个页面都都Html,在商品每时每刻都可能被更 ...