前言

  最近在弄个人的网站,偶然间发现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. [转]如何用git将项目代码上传到github

    注册账户以及创建仓库 要想使用github第一步当然是注册github账号了.之后就可以创建仓库了(免费用户只能建公共仓库),Create a New Repository,填好名称后Create,之 ...

  2. Robot Framework常用的操作库列表

    标准库是Robot Framework可以直接导入使用的库,包含以下几类: Builtin:包含经常需要的关键字.自动导入无需import,因此总是可用的 Dialogs:提供了暂停测试执行和从用户的 ...

  3. Mysql操作符号

    1.比较运算符:  =  相等  <> 不等于 != 这个也可以  >  大于  <  小于  >= 大于等于  <= 小于等于 2.逻辑运算符:  is null ...

  4. 安装MySQL需要注意的事项

    一.安装MySQL之后,怎样启动MySQL,以及登录.查看数据库操作? 用管理员身份运行cmd或power shell 二.当然有可能会出现不能在cmd或power shell中登录数据库账户 造成这 ...

  5. 2018SCin tsyzDay2 模拟赛-动态规划(简单的)

    内心OS:简单?????还是我太弱了. 期望得分:100+100+0+0+0+0+随机暴力的点==200 实际得分:0+100+10+0+10+0==120 您知道我第一题为什么错了嘛??文件在混乱中 ...

  6. 数位dp总结 之 从入门到模板

    转发自WUST_WenHao巨巨的博客 基础篇 数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数.所谓数位dp,字面意思就是在数位上进行dp咯.数位还算是比较好听 ...

  7. Codeforces Round #318 [RussianCodeCup Thanks-Round] (Div. 2)

    以后每做完一场CF,解题报告都写在一起吧   暴力||二分 A - Bear and Elections 题意:有n个候选人,第一个候选人可以贿赂其他人拿到他们的票,问最少要贿赂多少张票第一个人才能赢 ...

  8. 对char类型数组的英文字母进行冒泡排序

    import java.util.Arrays; import java.util.Scanner; public class Demo02 { public static void main(Str ...

  9. JS filters-ul li简单过滤

    功能要求:在input中输入字母,显示ul li中匹配的元素,隐藏不匹配的 <!DOCTYPE html> <html> <head> <meta chars ...

  10. ASP.NET中调用事务处理的方法

    /// <summary> /// 事务处理 /// </summary> /// <param name="strSql"></para ...