[css] 疑似属性のhasがsafariブラウザで動的に判定してくれない件

2023年4月7日

CSS トラブル

eyecatch とある仕事をしていた時に、聞いたこともないブラウザの不具合を見つけてしまうことがあります。 今回は、動的にデータベースから取得したリストを表示する処理を構築していた時の話です。 とりあえず、スマートフォンで見てみようと思い、QRコードで読み取った画面を見てみると、表示がされない・・・ 始めはajax処理がうまく言っていないのかと考えていたが、調べていくうちに、css記述の問題であることが分かった。 今回の犯人は、:has + :empty であることが判明したので、これは不具合としてブログに残しておくことにする。

簡易ソースコード公開

index.html

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"/> <title>has check</title> <link rel="stylesheet" href="style.css"/> <script src='load.js'></script> </head> <body> <h1>has test</h1> <div class="has-test"> <ul></ul> </div> </body> </html>

style.css

.has-test{ border:1px solid black; padding:10px; } .has-test:has(ul:empty){ border:1px dashed #aaa; }

load.js

function Ajax(options){ this.options = options || {} const xhr = new XMLHttpRequest() xhr.open( this.options.method || 'post', this.options.url, this.options.async ?? true) xhr.setRequestHeader( 'Content-Type', this.options.content_type || 'application/x-www-form-urlencoded' ) xhr.onload = this.loaded.bind(this) xhr.send(this.get_queries(this.options.query)) } Ajax.prototype.get_queries = function(queries){ if(!queries){return null} return Object.entries(queries).map(([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&'); } Ajax.prototype.loaded = function(e){ if(!this.options.success){return} this.options.success(e.target) } new Ajax({ url : 'data.json', method : 'get', success : ((res)=>{ const datas = JSON.parse(res.response) for(const data of datas){ const li = document.createElement('li') li.textContent = data.name document.querySelector('.has-test ul').appendChild(li) } }) })

data.json

[ { "name":"hoge" }, { "name":"fuga" }, { "name":"hage" } ]

表示

Google Chrome

Safari

Firefox

解説

おわかりいただけただろうか? ajax処理で、jsonデータを読み込み、そのリストデータをulタグ内にliタグを作って反映するプログラムです。 cssで、次のように書いてあるところがポイントで、 .has-test:has(ul:empty){ border:1px dashed #aaa; } ulタグがempty状態の場合に、borderの線を破線に切り替えています。 要するに、リストの内容が無い場合に、装飾を切り替える記述なんですが、safariは、その後リストの内容が入っているにも関わらず、破線状態になっているという結果です。 :has + :empty というのが特殊かと思ったけど、GoogleChromeで正常に処理されていることを考えると、safariブラウザの問題であると考えられます。 念には念を入れて、Firefoxで見てみたら、こちらも想定通りの表示。 ちなみに、これらのwebブラウザはMacOSのモノです。 そして、次のようにすることで、safariでも正常な状態にすることができました。 .has-test ul{ border:1px solid black; padding:10px; } .has-test ul:empty{ border:1px dashed #aaa; } でも、そもそもこれって階層が変わっているので、この事象を知っている状態で対応しなければいけません。

さらに深堀り

疑似属性の連携で不具合が生じる事が分かったので、ヘッダメニューなどでよく使う、checkbox:checkedも調査してみたいと思います。 上記のソースの変更部分のみ記載します。

index.html

<div class="has-test"> <input type='checkbox' name='test'/> <ul></ul> </div>

style.css

.has-test:has(input[type='checkbox']:checked){ border-color:red; } この場合は、うまく表示できました。 おそらく、イベント処理が走ったため、表示処理が更新されたんだと思います。

今回検証のまとめ

今回の検証で、safariでの疑似属性イベントの組み合わせで正常表示できない事が分かりました。 can i useでブラウザの対応状況を調べてみました。

has

empty

ハア?safariで組み合わせ対応ができてませんけど!!と毒付きたくなる気持ちを押さえて、こうした事象を知っておくことの方が前向きだと思いました。 そういえば、:has + :empty だけの問題ではなく、+ajaxも原因の一つでしたね。 これらの組み合わせ、意外とありえるシチュエーションなので、 要注意ですよ、:has + :empty + ajax