#7 2021年2月にツイートしたHTML/CSS/JavaScriptのTipsまとめ
2021年2月にツイートしたHTML/CSS/JavaScriptのツイートまとめです。見出しをクリックするとツイート元に遷移するので、気に入ったらフォロー・ファボ・リツイートお願いします。
最初の要素を選択する特殊なセレクタ
1.リストの li
要素のうち、1番目の li
要素を選択するときには :first-child
を使うと思いますが、場合によっては間違った書き方となってしまいます。
ul > li:first-child { ... }
:first-child
だと hidden
属性で非表示になっている要素にもスタイルが適用されてしまうため、本来は <li>2</li>
を選択して欲しいのですが、<li hidden>1</li>
が選択されます。
<ul>
<li hidden>1</li>
<li>2</li>
<li hidden>3</li>
<li hidden>4</li>
<li>5</li>
<li>6</li>
<li hidden>7</li>
<li>8</li>
<li>9</li>
</ul>
:not([hidden]) ~ :not([hidden])
は hidden
属性をもたない要素以降の hidden
属性を持たない要素群で、hidden
属性をもたない2番目以降の要素すべてを選択するセレクタです。
ul > :not(:not([hidden]) ~ :not([hidden])) { ... }
IE | Edge | Firefox | Chrome | Safari | Opera | iOS Safari | Android |
---|---|---|---|---|---|---|---|
- | 88 | 84 | 88 | 9 | 74 | 9 | 88 |
それに、:not()
がついているため、hidden
属性をもたない最初の要素である <li>2</li>
が選択されます。
ul > :not([hidden]) ~ :not([hidden]) { ... }
逆に最初の要素以外を選択するには :not()
を外せば大丈夫です。このセレクタならすべてのブラウザで使えるため、非対応ブラウザむけにCSSの上書きで対応することもできます。
::target-text擬似要素
2.Chrome 89からText Fragmentsのスタイルを ::target-text
で変更できます。
::target-text {
background: lightcyan;
}
:-webkit-autofillが標準化へ
3.input
要素などのオートフィル(自動入力)時に適用される :-webkit-autofill
が標準化され、Firefox 86から :autofill
が実装されました。
input:-webkit-autofill {
// WebKitブラウザ
}
input:autofill {
// Firefox 86から実装
}
オートフィル時の背景色
4.Chromeではオートフィル(自動入力)でフォームに値が設定されると、背景色が薄い青色(昔は黄色)になります。
デザイン性の高いサイトではオートフィル時の背景色が目立ちすぎてしまいます。
box-shadowで隠す
box-shadow
を使って内側の影で隠してしまう方法があります。この方法の欠点は背景が透明の場合には使えないことです。
input:-webkit-autofill {
box-shadow: 0 0 0 999px #fff inset;
}
transition-delayで遅らせる
:-webkit-autofill
擬似クラスでオートフィル時に transition-delay
に長い時間を指定して、実質背景色が表示されないようにする方法です。
input {
background: #fff;
}
input:-webkit-autofill {
transition-property: background-color;
transition-delay: 9999s;
}
ちなみに、input
要素に background
ではなく background-color: #fff;
とするとなぜかこの方法は使えなくなります。ですが、input
要素に background
だけ指定するケースは普通はなく、以下のように border
プロパティと一緒に使うことがほとんどだと思います。
input {
border: 1px solid #ced4da;
background: #fff;
}
input:-webkit-autofill {
transition-property: background-color;
transition-delay: 9999s;
}
border
プロパティと一緒に使えば、このテクニックは使えます。
clip-path: path()
5.モダンブラウザで clip-path
プロパティに path()
関数が使えるようになりました。
<img src="https://picsum.photos/1280/720">
img {
width: 250px;
height: 250px;
object-fit: cover;
clip-path: path('M213.1,6.7c-32.4-14.4-73.7,0-88.1,30.6C110.6,4.9,67.5-9.5,36.9,6.7C2.8,22.9-13.4,62.4,13.5,110.9 C33.3,145.1,67.5,170.3,125,217c59.3-46.7,93.5-71.9,111.5-106.1C263.4,64.2,247.2,22.9,213.1,6.7z');
}
IE | Edge | Firefox | Chrome | Safari | Opera | iOS Safari | Android |
---|---|---|---|---|---|---|---|
- | 88 | 71 | 88 | 10 | 74 | 10 | 88 |
scroll-behaviorの注意点
6.CSSでスムーズスクロールを実装するには scroll-behavior: smooth;
を使いますが、普通に指定するだけだと色んな問題が起きます。
Chromeでサイト内検索するときに矢印ボタンで移動していくと、その移動までもスムーズスクロールになってしまいます。
html:focus-within {
scroll-behavior: smooth;
}
擬似クラス :focus-within
は子孫要素の中に :focus
状態の要素があれば適用されます。それを利用して :focus
状態のときにスムーズスクロールされるようにします。
<a href="#bottom">下へ</a>
しかし、擬似クラス :focus-within
を使った解決策だとページ内リンクをクリックしたときにChromeとFirefoxでスムーズスクロールしなくなってしまいます。
@keyframes smooth-scroll-1 {
from, to { scroll-behavior: smooth; }
}
@keyframes smooth-scroll-2 {
from, to { scroll-behavior: smooth; }
}
html {
animation: smooth-scroll-1 1s;
}
html:focus-within {
animation-name: smooth-scroll-2;
scroll-behavior: smooth;
}
そこで、scroll-behavior: smooth;
を指定した2つのアニメーションを用意し、:focus
されたときにアニメーションを切り替えることで正しくスムーズスクロールされるようにしています。将来的にはサイト内検索の問題は解決されると思いますが、まだ時間がかかりそうです。
CSSハックを支える技術
7.
コンテナクエリ
8.ブラウザ幅のメディアクエリではなく、特定の要素の幅で場合分けできるコンテナクエリが検討中です。
フォームからフォーカスが外れたときの判定
9.focusout
イベントだとタブキーで遷移したときにも focusout
イベントが発生してしまいます。
<form>
<input type="text">
<input type="text">
<input type="text">
...
</form>
const form = document.querySelector('form')
form.addEventListener('focusout', event => {
// フォームからフォーカスが外れたとき
})
event.relatedTarget
を活用すればタブキーでの遷移を除外できます。
const form = document.querySelector('form')
form.addEventListener('focusout', event => {
if (form.contains(event.relatedTarget)) {
return
}
// フォームからフォーカスが外れたとき
})
addEventListener()とAbortController()
10.addEventListener()
で AbortController()
が使えるようになりました。これにより、ドラッグ&ドロップ実装のときのような removeEventListener()
を複数記述する必要がなくなって見やすくなります。
const element = document.querySelector('div')
// マウスのボタンを押し込んだとき
element.addEventListener('mousedown', event => {
// マウスの左クリック以外は無視
if (event.buttons !== 1) {
return
}
const {offsetX, offsetY} = event
// マウスを動かしているとき
const onMousemove = event => {
const {pageX, pageY} = event
element.style.left = `${pageX - offsetX}px`
element.style.top = `${pageY - offsetY}px`
}
// マウスのボタンを離したとき
const onMouseup = event => {
// mousedownするたびにイベントを追加しているので、ここで解除する
window.removeEventListener('mousemove', onMousemove)
window.removeEventListener('mouseup', onMouseup)
}
// elementにaddEventListenerすると、マウスを高速で動かしたときに動作しない
window.addEventListener('mousemove', onMousemove)
window.addEventListener('mouseup', onMouseup)
})
const element = document.querySelector('div')
// マウスのボタンを押し込んだとき
element.addEventListener('mousedown', event => {
// マウスの左クリック以外は無視
if (event.buttons !== 1) {
return
}
const {offsetX, offsetY} = event
const controller = new AbortController()
// マウスを動かしているとき
const onMousemove = event => {
const {pageX, pageY} = event
element.style.left = `${pageX - offsetX}px`
element.style.top = `${pageY - offsetY}px`
}
// マウスのボタンを離したとき
const onMouseup = event => {
controller.abort()
}
// elementにaddEventListenerすると、マウスを高速で動かしたときに動作しない
// 第3引数のsignalにcontroller.signalを指定することで、
// controller.abort()を呼び出すだけで両方のイベントを解除できる
window.addEventListener('mousemove', onMousemove, {signal: controller.signal})
window.addEventListener('mouseup', onMouseup, {signal: controller.signal})
})
Intl.ListFormat()
11.Intl.ListFormat()
を使うと、配列を言語にあわせてフォーマットしてくれます。多言語サイトを作るときなどに便利ですね。
const list = ['A', 'B', 'C', 'D']
const format1 = new Intl.ListFormat('en', {style: 'long', type: 'conjunction'})
console.log(format1.format(list)) // => A, B, C, and D
const format2 = new Intl.ListFormat('en', {style: 'short', type: 'disjunction'})
console.log(format2.format(list)) // => A, B, C, or D
const format3 = new Intl.ListFormat('en', {style: 'narrow', type: 'unit'})
console.log(format3.format(list)) // => A B C D
第1引数には言語を指定し、第2引数にはオブジェクトでオプションを指定します。style
には long
/short
/narrow
を、type
には conjunction
/disjunction
/unit
を指定できます。
// A, B, C, and D
const format1 = new Intl.ListFormat('en', {style: 'long', type: 'conjunction'})
// A, B, C, or D
const format2 = new Intl.ListFormat('en', {style: 'long', type: 'disjunction'})
// A, B, C, D
const format3 = new Intl.ListFormat('en', {style: 'long', type: 'unit'})
// A, B, C, & D
const format4 = new Intl.ListFormat('en', {style: 'short', type: 'conjunction'})
// A, B, C, or D
const format5 = new Intl.ListFormat('en', {style: 'short', type: 'disjunction'})
// A, B, C, D
const format6 = new Intl.ListFormat('en', {style: 'short', type: 'unit'})
// A, B, C, D
const format7 = new Intl.ListFormat('en', {style: 'narrow', type: 'conjunction'})
// A, B, C, or D
const format8 = new Intl.ListFormat('en', {style: 'narrow', type: 'disjunction'})
// A B C D
const format9 = new Intl.ListFormat('en', {style: 'narrow', type: 'unit'})
オプションによる出力の違いを一覧にしてみました。
fetch()とpreload
12.fetch()
関数で preload
したファイルを取得すると2度同じファイルがダウンロードされてしまいます。
<link rel="preload" href="./data.json" as="fetch">
fetch('./data.json')
.then(data => data.json())
.then(json => console.log(json))
2重ダウンロードを防ぐには fetch()
関数にオプションを指定します。
同一ドメインのとき
<link rel="preload" href="./data.json" as="fetch">
fetch('./data.json', {credentials: 'include', mode: 'no-cors'})
.then(data => data.json())
.then(json => console.log(json))
クロスドメインのとき
<link rel="preload" href="https://api.github.com/users/takamoso/repos" as="fetch" crossorigin>
fetch('https://api.github.com/users/takamoso/repos', {mode: 'cors'})
.then(data => data.json())
.then(json => console.log(json))
Pikaday
13.Pikadayは依存ライブラリなしの日付ピッカーライブラリです。
golden-layout
14.golden-layoutはコーディングエディタやPhotoshopのように1画面上で複数のウィンドウやタブを自由に移動できる機能を実装できるライブラリです。
a11y-dialog
15.a11y-dialogは軽量でアクセシブルなモーダル(ダイアログ)ライブラリです。
tippyjs
16.tippyjsは多機能なツールチップライブラリです。
redaxios
17.readaxiosはFetch APIを利用してaxiosをたったの800バイトで実装したライブラリです。
linkedom
18.linkedomはJSDOMのようなDOMパーサーライブラリです。
sql-formatter
19.sql-formatterはSQL文を見やすく整形してくれるライブラリです。
excellentexport
20.excellentexportはHTMLのテーブルをExcelまたはCSVファイルとして出力できるようにするライブラリです。
event-target-shim
21.event-target-shimは EventTarget()
と Event()
のPolyfillライブラリです。
Glassmorphism CSS Generator
22.Glassmorphism CSS Generatorは半透明でぼかしが入ったGlassmorphismのオンラインジェネレーターです。
Pattern Generator
23.Pattern Generatorはシームレスなパターンを生成できるオンラインジェネレーターです。
CodeCopy
24.CodeCopyはWebページ上のソースコードにコピーボタンを追加できるChrome/Firefox拡張機能です。あらかじめ登録されたドメイン以外にも、自分で追加できます。
Discussion