虽然要讲解的知识点是通用的,可是仍是要介绍下个人应用场景和测试环境。javascript
个人应用是一块使用Html Canvas开发的黑板,在黑板上实现简单的文字编辑功能。html
使用Canvas实现文字编辑器的细节这里就不讲了,原理大体都相同。一个必要条件是须要一个隐藏的textarea监听文字输入,由于canvas是没法记录文字选中、换行等信息的,也没有办法直接激活输入法和软键盘。textarea 以下:html5
复制代码
不少状况下,咱们须要实时监控文字输入的变化,下面是几种选择。java
监听keydown 和 keyup事件是最直接的方法,经过keyCode咱们能够获取按键值。chrome
this.hiddenTextArea.onkeyup = (e) => {
console.log("textKeyup",e.keyCode);
}
this.hiddenTextArea.onkeydown = (e) => {
console.log("textKeydown",e.keyCode);
}复制代码
input事件在keydown事件触发以后被触发,这是input类型元素使用的标准事件,表示有文字输入。canvas
从上图中咱们能够看到,触发的事件为InputEvent,从该事件对象的data属性中能够获取到当前输入的按键值。浏览器
正常状况下,每一次按键都会触发oninput事件。编辑器
该事件主要是为了兼容IE9及如下浏览器对oninput事件的处理。测试
在监听到 onpropertychange 事件后,可使用 event 的 propertyName 属性来获取发生变化的属性名称。ui
// Internet Explorer
function OnPropChanged (event) {
if (event.propertyName.toLowerCase () == "value") {
alert ("The new content: " + event.srcElement.value);
}
}复制代码
咱们把及时响应键盘按键输入单个按键表明的字符到文本框的行为称为直接输入模式,相对的非直接输入模式,一般是输入法拦截了按键消息以后的输入,以中文输入法为例,一般是回车或者空格后完成输入。
在非直接输入模式下,咱们期待的结果是当用户完成输入的时候(按了空格或者回车键)触发一次oninput事件。可是很不幸,每次按键都会触发oninput事件,这会致使咱们不知道用户正在输入的是否是中文,也不知道何时结束的输入,也就没办法及时对输入的中文进行处理。 为了解决这个问题,咱们看一下非直接输入状况下几个有用的属性和事件。
这两个属性是textarea对文字选中区域的标识,从0开始,简化分析,咱们只考虑正常文字输入,不考虑有选中的状况,首先是直接输入模式。
上图是我在oninput事件中打的日志,能够明显的看到每次oninput触发以后,selectionStart和selectionEnd的值都相同并且表示最后一个文本,视觉上是咱们看到的光标所在的位置,若是咱们挪动光标,这两个值也会变化。这里咱们提取两个关键特征,在及时输入的状况下:
下面咱们再看非直接输入模式。
从上图中,咱们能够看到在非直接输入模式下,在未完成输入以前,selectionStart一直为0,selectonEnd随着输入一直变化。完成输入时,selectionStart与selectonEnd值会相等。
从0.3.1的图中咱们能够看到拼音输入法输入过程当中,value值的变化,在完成输入以前这个值是由输入法控制的,完成以后,value的值会变为输入的文字内容。
这是一对事件,当非直接输入开始第一个按键的时候,触发compositionstart事件,非直接输入结束的时候触发compositionend事件, 在直接输入状况下,这两个事件都不会触发。咱们添加对这两个事件的监听:
this.hiddenTextArea.addEventListener('compositionstart', function () {
console.log("compositionstart");
that.isCompositionsting = false;
})
this.hiddenTextArea.addEventListener('compositionend', function () {
console.log("compositionend");
that.isCompositionsting = false;
})复制代码
观察上图的输出内容,各个事件的执行顺序为:
keydown-->compositionstart-->input-->keyup....-->input-->compositionend-->keyup。
如今咱们观察下在非直接模式下,按键的值。
this.hiddenTextArea.onkeyup = (e) => {
console.log("keydowncode",e.keyCode);
}
this.hiddenTextArea.onkeydown = (e) => {
console.log("keyupcode",e.keyCode);
}复制代码
经过上图,咱们能够看到不论你按下的是什么键,keycode都被重置为229了。固然这并非什么标准,不一样输入法的行为仍是不同的。不过目前咱们能接触到的中文输入法,正常状况下都是229。
经过上面的分析,咱们从新整理下及时响应非直接输入的思路。
下面以selectionStart 和 selectionEnd为例,判断中文输入的开始和结束(非完整代码)。
this.hiddenTextArea.oninput = (e) => {
if (textArea.selectionStart == textArea.selectionEnd) {
editor.bdCanvas.textEditor.currentText.text = textArea.value;
if (!isCompositionsting && 没有文字选中状况) {
//直接插入ascii字符
} else {
//插入文字(若是有部分文字被选中并替换)
//先删除被选中的文字
if (isCompositionsting) {//中文(中文输入法按空格以后,或者回车后输入的字符)
isCompositionsting = false;
} else {//英文、数字 (有选中内容被替换)
}
}
} else {//到这儿说明输入的是中文(中文输入法未按空格以前)
isCompositionsting = true;
}
}复制代码
上面的代码引入了isCompositionsting变量,结合selectionStart和selectionEnd来作文字处理。其余方法同理,这里就不过多讲解了。下面咱们来分析点异常状况。
这是我安装的最新的搜狗输入法,下面要说的非正常状况,只在这一个版本下会出现。
咱们在代码中对keydown,keyup,input,compositionstart和compositionend同时作事件监听,而后使用这个版本的搜狗输入法作输入。结果以下:
在未按下回车或者空格键以前,咱们看到:
再看结束输入时的状况:
上图红框内的内容为结束输入时的记录,此时触发一次input事件,selectionStart和selectionEnd相等。
这种状况,结束输入触发一次input,我却是认为很合理的作法,这样不少状况咱们不用关心是不是中文输入了,input的时候获取新的value,记录上一次的selectionstart就能够了。
这种特殊行为我没有具体研究是输入法自己的问题,仍是和浏览器、操做系统共同做用的结果。若是你编写相似的程序,须要额外注意下。
本篇文章只是分析记录了一些现象,并无什么技术含量,欢迎留言讨论。