Js的编辑事件
最近再做js编辑器, 发现编辑的事件是个课题
有可能使用的事件
- key
- keydown 最先被触发
- keypress 不会被中文触发. 输入了文字, 但是, 文字还没有上去.
- keyup 最后触发, 文字上去了已经.
- observer
- change
- input
实际测试key事件
- onkeyup, 其他ok,
- tab依旧会跳走
- enter, 会莫名加div(顽固的执行系统原有策略)
- delete/backspace会删除一个字符然后再执行后面的脚本.
- onkeydown
- 这里才是处理/s的地方, tab, enter, delete/backspace全都正常.
- 中文会触发, 我们只要不理他就好了.
- 很神奇, 我试验出来, space不响应这个触发. 应该还是处理的消耗问题, timeout可以解决, 参见: 2020-04-08
- 很神奇, 睡了一觉之后, 这个问题不见了, 果然睡眠改变命运.
- onkeypress
- 如果没有e.preventDefault();那么顺序是: keydown -> keypress -> keyup
- 如果做了e.preventDefault(); 那么顺序是: keydown -> keyup, 这里keypress不会被触发.
- 这个是即将废弃的事件mdn预告了
- 这个很多时候不能呼出的原因是浏览器纷纷有自己不支持的按键了, 各种按键都不能触发keypress了.
要忽略输入法这么做: (mdn都有答案了!!!!)
- https://www.fxsitecompat.com/en-CA/docs/2018/keydown-and-keyup-events-are-now-fired-during-ime-composition/
//https://developer.mozilla.org/en-US/docs/Web/Events/keydown
if (event.isComposing || event.keyCode === 229) {
return; //这样就可以忽略输入法的中间输入了.
}
键值的区别
keydown
andkeyup
provide a code indicating which key is pressed, whilekeypress
indicates which character was entered. For example, a lowercase “a” will be reported as 65 bykeydown
andkeyup
, but as 97 bykeypress
. An uppercase “A” is reported as 65 by all events.
空格
- 229代表输入法在处理这个按键.
- 只有safari有这个问题, 并且要开输入法才有. 英文状态没有问题.
- chrome没有这个问题.
- keydown会出现229, 如果是中文输入法. 这个表现其实大部分情况是完美的, 毕竟用户在选字的时候, 要的不是真的输入.
- keyup不论什么输入法, 都会是正常的code.
- 因此, 空格确实需要keyup, 这个结论错误, mdn有样例代码. 上面有.
tab
参见隔壁blog: 2019-02-27editable之后tab的处理
[ contenteditable="true"] {
/* 使用属性编辑器 */
border: 1px solid rgb(155, 72, 72, 0.5);
background: rgba(201, 167, 123, 0.555);
height: 80%;
display: inline-block;
/* 这个会导致不自动生成div啥的. 但是, 会有br*/
width: 80%;
/* 这一条需要加进来, 因为display: inline-block;会导致整个块缩起来 */
margin: auto;
padding: 10px;
-moz-tab-size: 40;
-o-tab-size: 40;
tab-size: 40;
white-space: pre-wrap; /*tab的关键性代码*/
}
testd.onkeydown = function (e) {
if (e.keyCode == 9) {
e.preventDefault();
document.execCommand('indent', false, null)
document.execCommand('insertText', false, 'xx ss ' + e.code + "| |t\tt");
}
}
键盘事件总结
keyboardevent顺序
- https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
- 一定要看英文版. 比如按键顺序:
keydown
keypress
这个已经是不建议使用的事件了keydown
keypress
- «repeating until the user releases the key»
- `keyup
键盘事件携带的属性code, keycode, key……
- keycode: 这个是最主要使用的. 键的值. 如果是输入法正在输入, 这里会得到’229’, 否则会得到对应的键值, 比如tab对应9.
- key: 键的字面值, 一个字符串, 比如’f’
- charcode: 这个有点奇怪, 一般都是0? 因为我是再keydown阶段看的.
- 这个值也已经不推荐使用了.
- 这个值再keypress时发生作用.
- 他本身如果起作用的话, 和keycode是一样的值, 我看到0, 是因为他真的没生效.
- code: 字符的字面值, 一个字符串, 比如: ‘keyF’
- 修饰键:
- altkey 布尔值
- ctrlkey 布尔值
- shiftkey 布尔值
- metakey 布尔值
- inputmethod输入法
- composed 一个布尔值 标明这个事件是否可以从shadow dom传递到dom. 这里出现了一个关键字shadow dom. 我们稍后研究一下.
- iscomposing 一个布尔值, 如果他是true, 那么代表他在compositionstart和compositionend之间.
compositionevent
- 这个事件是和输入法紧密关联的.
- 只要是文本构成系统(最有代表性的就是输入法了)发生作用的时候, 这一类事件就会生效.
CompositionEvent
- compositionstart, compositionupdate, compositionend, 这是三个阶段性事件.
至此, 我们已经可以很彻底的理解下面的代码:
editor.onkeydown = editinput;
function editinput(e) {
if (e.isComposing || e.keyCode === 229) {
return; //这样就可以忽略输入法的中间输入了. 使用input事件时这个并不生效
}
if (e.keyCode == 9) {
e.preventDefault();
document.execCommand('insertText', false, '\t');
//很神奇这里必须用转义符号, 直接贴入一个tab是不行的, 会自动当做空格处理.
}
}
特别提示一下, 虽然key类事件的顺序是公认的, 但是, 如果加上input和observer那么就还有些分歧, 目前w3c的讨论组还在讨论中.
input事件
input
- https://developer.mozilla.org/en-US/docs/Web/Events/input
- 这个事件等于change事件, 不过他是在editable元素上触发.
- editable没有change事件, 只有input事件, 确切的说是change事件不一定每次都被触发.
- input的触发没有问题, 但是: 没有keycode, key, charcode, code这些.
- 也就是说input没有判断输入法输入的能力????
beforeinput
- 在input被真正改变之前触发
- https://developer.mozilla.org/en-US/docs/Web/Events/beforeinput
- 这个依旧不可以, 并且这个不可以用onbeforeinput
editor.addEventListener("beforeinput", editinput); //可以这样注册事件.
function editinput(e) {
alert('onbefore');
}
- input和key事件的混用, google的chrome工程师mathias推荐尽量用input, 可以判断一下使用, 那么我的问题就是, input能服务输入法吗?
陈词总结: 如果要用input事件, 那么就要去写composition事件. addEventListener方式就可以写.
反思了一下, 不能半途而废, 我要把input写好.
仔细想了一下, input和observer貌似无法处理类似tab键这样的事???????
observer 事件 这篇blog太长了, 这个单独开个题吧: 2019-03-19mutationobserver
- https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
- object.observer已经是被淘汰的接口, 目前的建议是: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
- mutationobserver的处理优先级相当高, 早于正常的ui事件, 因此可以让整个处理更加流畅. Vue用了这个.
这里出现了一个小小的分支.
如何查看dom树
- chrome的elements视图就是dom树的视图.
- https://developers.google.com/web/tools/chrome-devtools/
- element这边右键可以选择break on, 然后相关事件发生就会被break,
- 这个是发生在mutationobserver
- 这个会显示在devtools->elements->dom breakpoints
- source那边也可以针对某个event进行breakpoints
事件这件事本身就值得研究一下
- 入门的事件文档: https://zhuanlan.zhihu.com/p/24620643
- 执行队列请控制后, js就到事件队列去找任务.
- 微任务排在宏任务之前,
- micro包括
- promise.then和
- mutationobserver.
- process.nextTick 优先级高于Promise, 这是一个node概念.
- macro包括
- timeout和
- 1/o
- 以及交互事件
- https://zhuanlan.zhihu.com/p/34229323
- 这个讲的比较清晰, 而是是按照w3c/what标准对译的, 用词比较准确.
- 而且他解释了vue使用mutationobserver的原因和方法.
- micro包括
- 疑问出现了: 可以自定义一个listener吗? 可以自定义一个event吗?
var event = new Event('build');
// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);
// Dispatch the event.
elem.dispatchEvent(event);
// custom event 也可以用来新建事件
var event = new CustomEvent('build', { 'detail': elem.dataset.time });
官方一个完整的示例:
<form>
<textarea></textarea>
</form>
const form = document.querySelector('form');
const textarea = document.querySelector('textarea');
// Create a new event, allow bubbling, and provide any data you want to pass to the "details" property
const eventAwesome = new CustomEvent('awesome', {
bubbles: true,
detail: { text: () => textarea.value }
});
// The form element listens for the custom "awesome" event and then consoles the output of the passed text() method
form.addEventListener('awesome', e => console.log(e.detail.text()));
// As the user types, the textarea inside the form dispatches/triggers the event to fire, and uses itself as the starting point
textarea.addEventListener('input', e => e.target.dispatchEvent(eventAwesome));
//甚至可以这么写:
form.addEventListener('awesome', e => console.log(e.detail.text()));
//这时候awesome还没创建呢.
textarea.addEventListener('input', function() {
// Create and dispatch/trigger an event on the fly
// Note: Optionally, we've also leveraged the "function expression" (instead of the "arrow function expression") so "this" will represent the element
this.dispatchEvent(new CustomEvent('awesome', { bubbles: true, detail: { text: () => textarea.value } }))
});
- 这里写的比mdn更清晰.
- https://javascript.info/dispatch-events
- https://www.sitepoint.com/javascript-custom-events/
- 要点
Event.preventDefault()
被调用的情况下. target.dispatchEvent会返回false. 注意这里还需要cancelable=true. 示例:
var div = document.getElementById("test");
div.addEventListener("custom", function(e) {
e.preventDefault();
});
div.innerHTML = div.dispatchEvent(new CustomEvent("custom", {
cancelable: true
}));
pre值得研究一下 后续blog 2019-3-19-pre
这个标签是三兄弟
- code 显示代码用的, 如果是多行代码, 建议放到pre里面.
- pre 其实就他最特殊, 显示很特殊.
- samp 展示输出用的, 可以嵌套kbd(代表用户输入).
shadow dom
- dom本身到了element就是原子了, shadow dom可以描述一个element.
- 举个例子就是video元素, 再里面有各种按钮和操作, 这都是shadow dom发挥作用的地方.
- shadow dom用一棵树来描述一个element.
- 这个element自身包含了code, css, js….也就是说, 形成了一个html闭包.
- shadow dom的本质是封装.
- shadow dom是custom elements的一部分.
- attach的时候选择open还是close?
- https://blog.revillweb.com/open-vs-closed-shadow-dom-9f3d7427d1af
- 其实很无聊, open模式是可以用shadowroot属性引用, close只能自己保留一个(attach)返回值引用.
- 这里介绍了更多关于: https://glazkov.com/2011/01/14/what-the-heck-is-shadow-dom/
custom element
- 自定义标签终于形成了完整的规范.
- https://developer.mozilla.org/en-US/docs/Web/Web_Components
- 整个标准叫web components, 它包含三部分:
- custom element, 一组js api, 可以自定义标签以及他的hebaviour,
- shadow dom, 一组js api, 可以定义一个元素的内部结构.
- html templates, html的模板标签, 可以在上面两个api里面重复使用.
- 基础做法
- 定义一个类或者函数.
- 注册这个custom element, 用 customelementregistry.define.
- 如果需要加入这个元素的shadow dom, 用element.attachshadow. 加入子元素, 时间监听event listener, 这些都可以用dom的标准方法做.
- 如果需要定义html模板, 使用和
标签. 用dom方法直接clone这个标签, 然后放到shadow dom里面. - 就像正常的html标签一样使用自定义标签.
functional 的定制元素
- https://github.com/hybridsjs/hybrids
- 为了简化自定义元素这件事, 大家开发出各种框架.