ISO 8601 覚書と正規表現
ISO 8601 覚書と正規表現
日付の表現
- 暦日付、年間通算日、週間日付の3種類だが、年間通算日と週間日付は除外する
暦日付
- 書式は YYYY-MM-DD または YYYYMMDDを許容する
- YYYYは4桁(0000-9999)、MMは2桁(01-12)、DDは2桁(01-31)とする
- 下位省略表記については、YYYY-MM、YYYY を採用する
let REG_YYYYMMDD = /^([0-9]{4})(?:\-?(0[1-9]|1[0-2])(?:\-?(0[1-9]|[12][0-9]|3[01]))?)?$/;
- 上位省略表記については、--MM-DD、--MM、---DDを許容する
let REG_YYYYMMDD = /^(?=(?:\d+(?:\-\d+(?:\-\d+)?)?|\-\-\d+?\-?\d+))([0-9]{4})?\D*(0[1-9]|1[0-2])?\D?(0[1-9]|[12][0-9]|3[01])?$/;
正規表現での考え方
前段階で2つの書式をチェックして、後半で数字をチェックする。
reg_exp.exec (string) => Array (4) [*****, YYYY, MM, DD]
更に拡張して元号年月日を許容する
- 文字列の先頭 "西暦|明治|大正|昭和|平成|令和|M|T|S|H|R" を識別する。西暦年は4桁、他の元号は2桁を許容する
- 明治初期は陰暦なのでその年代の使用には注意を要する。
- 漢字交じりの日付文字列に限り数値の前後の空白を許容する。
- 元号年は2桁の、"明治99年1月1日"を許容する。
- 上位省略表記については除外する
let REG_GYYYYMMDD = /^(?=(?:(?:[MmTtSsHhRr]?\d{1,2}|\d{4})(?:\-?\d+(?:\-?\d+)?)?|\s*(?:西暦\s*\d{4}|(?:明治|大正|昭和|平成|令和)\s*\d{1,2})\s*年\s*(?:\d+\s*月\s*(?:\d+\s*日\s*)?)?))\s*([^\d\s]+)?\s*([0-9]{1,4})\D*\s*(0?[1-9]|1[0-2])\D*\s*(0?[1-9]|[12][0-9]|3[01])\D*\s*$/; console.log (REG_GYYYYMMDD.exec ("西暦 2021 年 3 月 1 日")); //=> Array(5) ["西暦 2021 年 3 月 1 日", "西暦", "2021", "3", "1" ]
時刻の表現
- 書式は HH:MM:SS、HHMMSS、HH:MM、HHMM、-MM:SS、-MMSS、--SS を許容する
■
今月で一先ず 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>