たまたま数週間前に socket.io のページを開いていて さっきいらないタブを閉じていたら新しくなってるのに気づきました
これまでのページのドキュメントってすごく読みづらくて 毎回 github の方で見ていたので読みやすくなって助かります
と言っても socket.io を使うことがそうそうないのですけどね
ページだけじゃなくて socket.io 自体もメジャーアップデートしたのかなと思いましたがバージョンは前見たときと一緒でした
document.body.append(`<div class="x"><span>1</span>23</div><div>45</div>`)
って書きたい
innerHTML ならできるけど append にはできない
これだとそのまま文字列として追加される
一旦 innerHTML に入れて全部の子要素を追加するの面倒
なので HTML テキストを DocumentFragment 化するもの
const fr = html => Object.assign(document.createElement("template"), {innerHTML: html}).content
これを使って
document.body.append(fr(`<div class="x"><span>1</span>23</div><div>45</div>`))
document.body.append(fr`<div class="x"><span>1</span>23</div><div>45</div>`)
() 書くの面倒ならなくしてテンプレートストリングのタグとしても使える
ただし間に ${} による埋め込みがないときだけ
せっかくだからタグならではの機能でエスケープ機能を入れる
const html = (strs, ...values) => {
const esc = value => Object.assign(document.createElement("div"), { textContent: value }).innerHTML
const fmt = value => value != null
? (value.html ? value.html : value.text ? esc(value.text) : esc(value))
: ""
return fr(strs.reduce((a, e, i) => a + fmt(values[i - 1]) + e))
}
これを使うと
document.body.append(html`<div class="x"><span>1</span>23</div><div>45</div>`)
document.body.append(html`<div class="x"><span>${1}</span>${"<br>"}${{html:"<b>bold</b>"}}</div><div>45</div>`)
「<br>」はエスケープされて文字列として表示されて 「<b>」 は太文字になる
最近は静的ファイルの配信だけじゃなくてサーバサイドプログラムも動くアプリケーションを無料公開できるサービスというのも見かけますね
今回見つけたのは月 10 万リクエストまで無料っていうものです
10 万もあれば小さいものなら余裕かなって思ってましたが
1 日 2000 PV として 1 PV で JavaScript や CSS や画像ファイルなど色々含めて 20 リクエストとしたら
1 日 4 万リクエスト (2000 * 20)
30 日だと 120 万リクエストもあります
思ったより少ないですね
こういうリクエスト数の制限があるなら画像なども含めてバンドルして html ファイル 1 つだけで全部入りっていうのが良いのかも
あと SPA にして毎回サーバの通信が不要なものはクライアント側だけで画面切り替えたり そういう普通はしなくていい工夫が必要になったりしそう
今回見つけたのは月 10 万リクエストまで無料っていうものです
10 万もあれば小さいものなら余裕かなって思ってましたが
1 日 2000 PV として 1 PV で JavaScript や CSS や画像ファイルなど色々含めて 20 リクエストとしたら
1 日 4 万リクエスト (2000 * 20)
30 日だと 120 万リクエストもあります
思ったより少ないですね
こういうリクエスト数の制限があるなら画像なども含めてバンドルして html ファイル 1 つだけで全部入りっていうのが良いのかも
あと SPA にして毎回サーバの通信が不要なものはクライアント側だけで画面切り替えたり そういう普通はしなくていい工夫が必要になったりしそう
this.querySelector(".elem").addEventListener("click", this.onClick.bind(this))
// とか
const tpl = html`<button on-click="${this.onClick.bind(this)}"></button>`
毎回 bind 書くのってすごく面倒
なので自動でするようにしました
class ExampleElement extends HTMLElement {
constructor() {
super()
for (const [key, fn] of Object.entries(this.listeners)) {
this[key] = fn.bind(this)
}
}
get listeners() {
return {
onClick() { console.log(this) }
}
}
example() {
this.querySelector("button").addEventListener("click", this.onClick)
}
}
listeners の getter でリスナ関数を返すようにする
コンストラクタで listeners で取得できるの全てに this を bind してプロパティに設定する
example メソッドのように 使うとき bind が不要になる
コンストラクタの処理は共通のベースクラスにでも書いておくといい感じ
コンストラクタで何もしない版
class ExampleElement extends HTMLElement {
get listeners() {
return {
onClick: () => { console.log(this) }
}
}
example() {
this.querySelector("button").addEventListener("click", this.listeners.onClick)
}
}
listeners を挟むけどわかりやすいといえばわかりやすい
アロー関数にしたらなにもしなくても this がクラスのインスンタンスに固定される
this.listeners = this.listeners
しておくほうがリスナ関数が同じ値になっていいかも
毎回のようによくわからなくなるのでまとめ
IN
IN で指定したどれかに行の値が一致すると true
NOT はどれにも一致しないと true
単純に IN にマッチしない行が true になる
「x NOT IN y」 と 「NOT (x IN y)」 は一緒
ANY
通常は IN と一緒
postgresql の場合は配列を指定するので $1 などのパラメータ化できるのが特徴
<> を使うと配列の要素のどれにもマッチしない行が true になる
「x <> ANY(y)」 と 「NOT (x = ANY(y))」 は別
「NOT (x = ANY(y))」 を使うと 「x NOT IN y」 と同じになる
SOME
ANY と全く同じ
ALL
配列の全てに一致する行が true になる
<> するとひとつでも一致しないのがあると true になる
「x <> ALL(y)」 と 「NOT (x = ALL(y))」 は別
「x <> ALL(y)」 と 「NOT (x = ANY(y))」 が同じになる
IN
IN で指定したどれかに行の値が一致すると true
NOT はどれにも一致しないと true
単純に IN にマッチしない行が true になる
「x NOT IN y」 と 「NOT (x IN y)」 は一緒
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c IN (1, 2)
-- 1, 2
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c NOT IN (1, 2)
-- 3, 4, 5
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE NOT (c IN (1, 2))
-- 3, 4, 5
ANY
通常は IN と一緒
postgresql の場合は配列を指定するので $1 などのパラメータ化できるのが特徴
<> を使うと配列の要素のどれにもマッチしない行が true になる
「x <> ANY(y)」 と 「NOT (x = ANY(y))」 は別
「NOT (x = ANY(y))」 を使うと 「x NOT IN y」 と同じになる
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c = ANY(ARRAY[1, 2])
-- 1, 2
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c <> ANY(ARRAY[1, 2])
-- 1, 2, 3, 4, 5
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c <> ANY(ARRAY[1, 1])
-- 2, 3, 4, 5
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE NOT (c = ANY(ARRAY[1, 2]))
-- 3, 4, 5
SOME
ANY と全く同じ
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c = SOME(ARRAY[1, 2])
-- 1, 2
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c <> SOME(ARRAY[1, 2])
-- 1, 2, 3, 4, 5
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c <> SOME(ARRAY[1, 1])
-- 2, 3, 4, 5
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE NOT (c = SOME(ARRAY[1, 2]))
-- 3, 4, 5
ALL
配列の全てに一致する行が true になる
<> するとひとつでも一致しないのがあると true になる
「x <> ALL(y)」 と 「NOT (x = ALL(y))」 は別
「x <> ALL(y)」 と 「NOT (x = ANY(y))」 が同じになる
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c = ALL(ARRAY[1, 2])
-- none
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c = ALL(ARRAY[1, 1])
-- 1
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE c <> ALL(ARRAY[1, 2])
-- 3, 4, 5
SELECT c
FROM unnest(ARRAY[1,2,3,4,5]) as c
WHERE NOT (c = ALL(ARRAY[1, 2]))
-- 1, 2, 3, 4, 5
performance タブ見るの面倒
JavaScript で測りたい
これだと DOM 更新のみ
この後に画面更新があるので 重いページでは ↑ は数 ms なのに画面に反映されるまで数秒固まったりする
非同期処理をすると画面更新後に行われる (ことが多い)
ときどき画面更新より前に処理されて ↑ は数 ms となって画面更新は 1 秒後だったりする
結果見たらわかるけど あんまり重くないと判断しづらい上に確実じゃないのはいまいち
Promise.resolve による非同期はたぶん確実に画面更新前に実行される
JavaScript で rendering の後にわかる情報を取得しようとするとそこでブロックしてくれる
JavaScript で測りたい
console.time("dom")
div.prepend(document.createElement("an-element"))
console.timeEnd("dom")
これだと DOM 更新のみ
この後に画面更新があるので 重いページでは ↑ は数 ms なのに画面に反映されるまで数秒固まったりする
非同期処理をすると画面更新後に行われる (ことが多い)
console.time("rendering")
setTimeout(() => console.timeEnd("rendering"), 0)
ときどき画面更新より前に処理されて ↑ は数 ms となって画面更新は 1 秒後だったりする
結果見たらわかるけど あんまり重くないと判断しづらい上に確実じゃないのはいまいち
Promise.resolve による非同期はたぶん確実に画面更新前に実行される
console.time("rendering")
Promise.resolve().then(() => console.timeEnd("rendering"))
JavaScript で rendering の後にわかる情報を取得しようとするとそこでブロックしてくれる
console.time("dom")
div.prepend(document.createElement("an-element"))
console.timeEnd("dom")
console.time("rendering")
div.getClientRects()
console.timeEnd("rendering")
// dom: 0.0810546875ms
// rendering: 1819.3779296875ms
Chrome で new Date() の結果表示したら
韓国になってる
時差一緒だから困ることはないけどなんでだろう?
new Date()
// Mon Jul 09 2018 04:29:43 GMT+0900 (韓国標準時)
韓国になってる
時差一緒だから困ることはないけどなんでだろう?
DocumentFragment に innerHTML はない
ただのプロパティだから入れても子要素が作られない
既存の DocumentFragment に入れたいなら div などに一旦入れて append
新規に作るなら template タグを使う
const df = document.createDocumentFragment()
df.innerHTML = `<p>text</p><div>div</div>`
console.log(df.innerHTML)
// "<p>text</p><div>div</div>"
console.log(df)
// #document-fragment
ただのプロパティだから入れても子要素が作られない
既存の DocumentFragment に入れたいなら div などに一旦入れて append
const div = document.createElement("div")
const df = document.createDocumentFragment()
div.innerHTML = `<p>text</p><div>div</div>`
df.append(...div.childNodes)
console.log(df)
// #document-fragment
// <p>text</p>
// <div>div</div>
新規に作るなら template タグを使う
const template = document.createElement("template")
template.innerHTML = `<p>text</p><div>div</div>`
console.log(template.content)
// #document-fragment
// <p>text</p>
// <div>div</div>
elem.tagName だと DIV みたいな大文字になる
小文字化面倒だと思ってたら localName だと小文字でとれる
「For example, in the qualified name "ecomm:partners", "partners" is the local name and "ecomm" is the prefix」
https://developer.mozilla.org/en-US/docs/Web/API/Element/localName
って書いてるのに
コロン含めて全部取れる
小文字化面倒だと思ってたら localName だと小文字でとれる
document.createElement("div").localName
// "div"
document.createElement("div").tagName
// "DIV"
document.createElement("div").nodeName
// "DIV"
「For example, in the qualified name "ecomm:partners", "partners" is the local name and "ecomm" is the prefix」
https://developer.mozilla.org/en-US/docs/Web/API/Element/localName
って書いてるのに
document.createElement("ecomm:partners").localName
// "ecomm:partners"
コロン含めて全部取れる