JavaScript エンターキーで移動する。その3
隠された要素は飛ばす。
[CTRL]+[Enter]では、 checked にする
テキストエリア内での処理を追加
IMEの未変換文字がある状態での Enter キーの対応
ルートの設定が個別にできること
<!DOCTYPE html> <title></title> <meta charset="utf-8"> <style> </style> <body> <form id="F"> <p> <input name="a" value="a"> <input name="b" value="b" type="hidden"> <input name="c" value="c"> <span style="display:none;"> <input name="d" value="d" id="d"> </span> </p> <textarea name="j" cols="40" rows="10">fcvnruewihvnbre vfreuoih\vreiojnreoi vreiojnreoivreiojnreoi </textarea> <p> <input name="e" value="e"> <input name="f" value="f" id="f"> </p> <input type="reset"> <p> <input type="radio" name="g" value="g0" checked> <input type="radio" name="g" value="g1"> <input type="radio" name="g" value="g2"> </p> <p> <input type="checkbox" name="h" value="h0" checked> <input type="checkbox" name="h" value="h1"> <input type="checkbox" name="h" value="h2"> </p> <p> <select name="i"> <option value="i0">i0</option> <option value="i1">i1</option> <option value="i2">i2</option> </select> </p> </form> <script> class ExEnter { constructor (root = document.documentElement, option = { }) { this.root = root; this.walker = document.createTreeWalker (root, NodeFilter.SHOW_ELEMENT, this.filter, true); this.option = Object.assign ({ }, this.constructor.defaultOption, option); //IME日本語入力中で未変換文字があり Enterが押されたことを感知するには //keypress イベントがが実行されないことを利用する this.imeFlag = null; //ime } //ルートの中の最初の要素を返す get firstElement () { this.walker.currentNode = this.root; return this.walker.firstChild (); } //ルートの最後の要素を返す get lastElement () { this.walker.currentNode = this.root; return this.walker.lastChild (); } //要素を移動する move (target, direction = false) { const walker = this.walker, isLoop = this.option.loop; this.walker.currentNode = target; let e = (direction) ? walker.previousNode () || (isLoop ? this.lastElement: null) : walker.nextNode () || (isLoop ? this.firstElement: null); e && e.focus (); } filter (node) { if (/^(INPUT|TEXTAREA|SELECT|BUTTON)$/.test (node.tagName)) if (! node.disabled && ! node.readOnly) { if (! isHide (node)) return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_SKIP; //__ function isHide (node) { for (let e = node, s; e; e = e.parentNode) { if (e === document.body) break; s = getComputedStyle (e, null); if ('none' === s.display || 'hidden' === s.visibility || !parseFloat (s.opacity || '') || !parseInt (s.height || '', 10) || !parseInt (s.width || '', 10) ) return true; } return false; } } //_____ //テキストエリアのキャレットの位置で分割(改行)する textareaSpliter (e) { let v = e.value, a = v.substring (0, e.selectionStart); e.value = a + '\n' + v.substr (e.selectionEnd); e.selectionStart = e.selectionEnd = a.length + 1; } //[CTRL]が押されていれば、適用させる apply (e, sw, prev) { let r = false; //要素に対して操作を行ったか switch (e.type) { case 'radio': case 'checkbox' : if (sw) { e.checked = !e.checked; r = this.option.stay; } break; case 'button' : case 'submit' : case 'reset' : if (sw) { let event = document.createEvent ('MouseEvents'); event.initEvent("click", false, true); e.dispatchEvent (event); r = this.option.stay; } break; case 'textarea' : if ((sw ^ this.option.textarea_enter) && !prev) { this.textareaSpliter (e); r =true; } break; } return r; } //_____ handleEvent (event) { this[event.type+ 'Handler'].call (this, event, event.target); } keyupHandler (event, e) { let { code, shiftKey, ctrlKey } = event; if ('Enter' !== code) return; if (! this.imeFlag) //IMEの動作で未変換中の文字があると判断してスルー return event.preventDefault (); this.imeFlag = null; if (! this.apply (e, ctrlKey, shiftKey)) this.move (e, shiftKey); } keypressHandler (event, e) { if (/^(submit|reset|button|textarea)$/.test (e.type)) event.preventDefault (); this.imeFlag = true;//未変換文字がある場合の対処として } //________ static defaultOption = { stay: false, //true: radio,checkbox,submit,button を作用させた後に自動的に移動する textarea_enter: false, //true:テキストエリア内では改行する ,false:[Ctrl]+[Enter]で改行 loop: true, //要素を巡回する }; static create (root = document.documentElement, option = { }) { let obj = new this (root, option); root.addEventListener ('keyup', obj, false); root.addEventListener ('keypress', obj, false); return obj; } } ExEnter.create (); </script>