■
今月で一先ず teratail で回答するのはやめにしよう!
ISO 8601 継続時間表記を正規表現で抜き出す
ISO 8601 日時の期間文字列から W, Y, M, D, h, m, s を抜き出す。
書式は、P[n]Y[n]M[n]DT[n]H[n]M[n]S
または、P[n]W
/^P(?=(?:\d+(?:\.\d+)?W|(?:\d+(?:\.\d+)?[YMD]){1,3}(?:T(?:\d+(?:\.\d+)?[HMS]){1,3})?|T(?:\d+(?:\.\d+)?[HMS]){1,3})$)(?:(.+)W|(?:(.+)Y)?(?:([\d\.]+)M)?(?:(.+)D)?)(?:T(?:(.+)H)?(?:(.+)M)?(?:(.+)S)?)?$/
Date オブジェクトと継続時間表記の文字列の加算
function addDateDuration (date = new this, iso8601_duration_string, scale = 1) { const //週の値は整数に制限[0-53], Y,M,D,h,m,s 共に少数を許容する REG_ISO8601_DURATION = /^P(?=(?:\d+(?:\.\d+)?W|(?:\d+(?:\.\d+)?[YMD]){1,3}(?:T(?:\d+(?:\.\d+)?[HMS]){1,3})?|T(?:\d+(?:\.\d+)?[HMS]){1,3})$)(?:(.+)W|(?:(.+)Y)?(?:([\d\.]+)M)?(?:(.+)D)?)(?:T(?:(.+)H)?(?:(.+)M)?(?:(.+)S)?)?$/, SECONDS = [ 86400, 3600, 60, 1 ], ONE_DAY = 365.2422 / 12, { floor: INT, ceil: CEIL } = Math; let parm = REG_ISO8601_DURATION.exec (iso8601_duration_string); if (parm) { let [, W, Y, M, D, h, m, s] = parm.map (n=> parseFloat (n) || 0), YM = Y * 12 + M,//Y,M の端数はDに加算して切り捨てる (時分秒に影響しない) ms = [D + W * 7 + INT ((YM % 1) * ONE_DAY), h, m, s].reduce ((a, b, i)=> a + b * SECONDS[i], 0) * 1000; date.setMonth (date.getMonth () + INT (YM * scale)); date.setTime (date.getTime () + INT (ms * scale)); return date; } else throw new Error ('Error!!: 解析できない文字列です。/:' + iso8601_duration_string); }
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>
JavaScriptの質問に答えているだけでは、自分のスキルはあまり向上しない
なんとなくそう思った。
JavaScriptでスライドを作る!しかもスクリプトは1行
基本的な考え方
- アニメーションはスタイルシートで行い。対象の要素群の先頭にだけ適用する。
- アニメーションが終了するとイベントが発生するのでそれを監視する。
- イベントの終了した要素を、その要素群の最後尾に移動する。
寺尾での回答
https://teratail.com/questions/321270
<style> div { width: 800px; height: 200px; overflow: hidden; margin: 0 auto; } ul { white-space: nowrap; list-style: none; padding: 0; margin: 0; } ul li { display: inline-block; width: 400px; height: 200px; margin: 0; padding: 0; } li:first-of-type { animation-name: expansion; animation-duration: 2s; animation-timing-function:ease; animation-delay: 2s; animation-iteration-count: 1; animation-direction: normal; animation-fill-mode: backwards; animation-play-state: running; } @keyframes expansion{ 0% { margin-left:-200px; } 100% { margin-left:-600px; } } </style> <body> <div> <ul> <li><img src="https://placehold.jp/400x200.png" alt=""> <li><img src="http://placehold.jp/24/cc9999/993333/400x200.png" alt=""> <li><img src="https://placehold.jp/400x200.png" alt=""> <li><img src="http://placehold.jp/24/cc9999/993333/400x200.png" alt=""> <li>WHAT's NEW! ^^; WHAT's NEW! ^^; WHAT's NEW! ^^; </ul> </div> <script> document.querySelector ('ul') .addEventListener ('animationend', ({target: e})=> e.parentNode.appendChild (e), false); </script>
SELECT[multiple]:selected を取得する方法を発明した?
そもそも SELECT:selected なんてものはない!
そこで change イベントで select[multiple] をすべて監視し、selected になったもに
"data-selected" を付与しておけば、いつでも querySelectorAll で取得できる。
もちろん、クエリーにもそれを付与しなければならないが。
let select = document.querySelectorAll ( 'select:not([multiple]), select[multiple] option[data-selected]' );
考え方は、select[multiple]を除外してその子要素の option 要素を集める
option 要素の親要素をたどれば select 要素に行き着く。
そこで name を取得すればOK!
Memo:
select.selectedOptions があることを知った。
FORM要素(とは限らない)から、有効な値を取り出すには・・・
これで、 radio, checkbox, select[multiple] から有効な値を取得することができるようになった。
<!DOCTYPE html> <meta charset="UTF-8" /> <title>Document</title> <style> </style> <body> <form> <p><input name="A" value="a"> <p><input name="B" value="b" type="hidden"> <p><output name="C" value="cccc">c</output> <p><textarea name="D">d</textarea> <p> <input name="E" value="e0" type="radio">E0 / <input name="E" value="e1" type="radio" checked>E1 / <input name="E" value="e2" type="radio">E2 <p> <input name="F" value="f0" type="checkbox" checked>F0 / <input name="F" value="f1" type="checkbox">F1 / <input name="F" value="f2" type="checkbox" checked>F2 <p> <select name="G"> <option value="g0">G0</option> <option value="g1" selected>G1</option> <option value="g2">G2</option> </select> </p> <p> <select name="H" multiple id="H"> <option value="h0" selected>H0</option> <option value="h1">H1</option> <option value="h2" selected>H2</option> </select> </p> <input type="button" value="Test" onclick="test()"> </form> <div> <textarea id="T" cols="60" rows="30"></textarea> </div> <script> class SurveillanceSELECT { constructor (root = document, key) { this.root = root; this.key = 'data-' + key; } switch (select) { for (let op of select.options) { op.selected ? op.setAttribute (this.key, '') : op.removeAttribute (this.key); } } setting () { Array.from ( this.root.querySelectorAll ('select[multiple]'), sel => this.switch (sel) ); } handleEvent (event) { let e = event.target; if ('select-multiple' === e.type) this.switch (e); } static create (root = document) { const key = 'selected'; let obj = new this (root, key); obj.setting (); root.addEventListener ('change', obj, false); return obj; } } SurveillanceSELECT.create (); //_________________________________ function test () { const q0 = '*[name]:not([type="checkbox"]):not([type="radio"]):not(select[multiple]),*[name]:checked,select[name][multiple] option[data-selected]'; let es = document.querySelectorAll (q0); T.value = [...es].map(e=>[(e.nodeName == 'OPTION' ? e.parentNode: e).name, e.value].join (" : ")).join (" \n"); } </script>
location.hash から値を取り出してオブジェクト型で返す
location.hash から値を取り出してオブジェクト型で返す
Ajaxで使うかもしれない
let obj = hash2obj ("http://hoge.com#label?a=123&b="); obj = { hash : '#label', id : 'label', value: { a: 123, b: undefined }, query : '?a=123&b=' }
function hash2obj (arg = window.location.hash) { const reg0 = /^(#([^\?\;\=]*)?)(\?.*)?/, //hash, id, paramater に切り分ける reg1 = /\??([^\=\&\;\?]+)(?:=?([^\&\;$]*)?(?:\&|\;|$))/g; // name = value if (arg = reg0.exec (arg)) { let [, hash, id, query] = arg, value = { }; for (let r; r = reg1.exec (query); value[r[1]] = r[2]) ; return { id, hash, query, value }; } else return null; }
オブジェクトからHTML要素を構成する
//obj = { tag: '????', attr: { key: value, ... }, text: 'sample text', cildlen: [obj0, obj1, ... ]} let arg = [ { tag: 'ol', attr: { id: 'hoge', className: 'abc def', childlen: [ //以下は同じ表示 { tag: 'li', text: 'ABCDEFG' }, { tag: 'li:ABCDEFG' }, { tag: 'li', childlen: ['ABCDEFG'] ] }, { tag: 'ol#hoge .abc .def' } //上の ol 要素と同じ意味 ]; document.body.appendChild (makeElement (arg));
code:
function makeElement (arg, target = document.createDocumentFragment ()) { //tag: str -> tag #id .class : text に切り分ける const reg0 = /^\s*([a-z]+)\s*(?:#([a-z][0-9a-z]*))?\s*(\.[^\s\.]+(?:\s+\.[^\s\.]+)*)?\s*(?::?\s*(.*?)\s*)$/i, reg1 = /^\s+|\s{2,}|\s+$/g,//前後の空白、中間の2文字以上の空白を削除 reg2 = /\./g; switch (Object.prototype.toString.call (arg).slice(8, -1)) { //引数の型で処理 case 'Object' : let { tag = null, attr = { }, childlen = null, text = null } = arg, [ tagName, id, className, text1 ] = reg0.exec (tag), attr1 = { }, e; if (null == tagName) throw new Error ('tagName がありません'); e = target.appendChild (document.createElement (tagName));//要素作成 if (id) attr1.id = id; if (className) attr1.className = className.replace (reg1,'').replace (reg2,' '); Object.assign (e, attr, attr1);//属性上書き (text1 != null)&& ary2Element (text1, e);//文字列 (text != null)&& ary2Element (text, e);//文字列 (childlen)&& ary2Element (childlen, e); //子要素の追加 break; case 'Array' : let fgm = document.createDocumentFragment (); arg.forEach (a=> ary2Element (a, fgm)); target.appendChild (fgm); break; case 'undefined' : case 'Null' : arg = '';//break しない default : target.appendChild (document.createTextNode (String (arg))); break; } return target; }
配列からテーブルを構成する
//配列をテーブルに変換する(テキストの先頭を判別して th/td, colspan, rowspan, class を指定できる) function ary2TBody (ary, tbody = document.createElement ('tbody'), fg_del = false) { const //#[4,2].abc .def AAAA => <th colspan="4" rowspan="2" class="abc def">AAAA</th> reg0 = /^(#)?(?:\[(\d+)?(?:\,(\d+)?)?\])?\s*(\.[^\s\.]+(?:\s+\.[^\s\.]+)*)?\s*(.*?)$/, reg1 = /^\s+|\s{2,}|\s+$/g,//前後の空白、中間の2文字以上の空白を削除 reg2 = /\./g; if (fg_del) [...tbody.childNodes].forEach (e=> e.remove ());//子要素の削除 for (let row of ary) { let tr = tbody.appendChild (document.createElement ('tr')); //tbody.insertRow ();//fgmを許容するため for (let cell of row) { let str = null == cell ? '': String (cell);// null == undefined => true let [, hash, colSpan = 1, rowSpan = 1, className = '', textContent ] = reg0.exec (str); let td = tr.appendChild (document.createElement (hash ? 'th': 'td')); className = className.replace (reg1,'').replace (reg2,' ');//2つ以上のクラスに対応するため Object.assign (td, { colSpan, rowSpan, className, textContent}); } } return tbody; }
配列からテーブルを作成する(単純高速版)
//配列をテーブルに変換する(高速・フラグメントを tbody に指定できない) static ary2QTBody (ary = [ ], tbody = document.createElement ('tbody')) { for (let row of ary) { let tr = tbody.insertRow (); for (let cell of row) { tr.insertCell ().textContent = cell; } } return tbody; //return ary.reduce((a,b)=>(b.reduce((c,d)=>(c.insertCell().textContent=d,c),a.insertRow()),a),tbody); }