管理しているクライアントさんのサイトについて、こんなメールが届きました。
スマホでヘッダーメニューを一度タップすると、文字がほとんど見えなくなります。
ページを閉じても戻りません。他のスマホでも同じでした。
確認すると、たしかにスマホのトップページだけで起きます。PCでは問題なし。
ここから半日がかりの犯人探しが始まりました。
症状:タップしたメニューだけが消える
- スマホ表示のトップページだけで起きる
- 一度タップしたメニュー項目の文字が、背景と同じ色になって見えなくなる
- ページを開き直しても戻らない
「一度タップすると」「開き直しても戻らない」。この2つがヒントでした。
ブラウザには 訪問済みのリンク を記録する仕組みがあり、CSSの :visited で色を変えられます。訪問履歴が残る限り、色も戻りません。
原因1:たった1行の a:visited
検証ツール(DevTools)でメニューの文字を調べると、こんなCSSが効いていました。
a:visited { color: inherit; }
「訪問済みのリンクは、親要素の文字色を使う」という指定です。
メニューの文字色は親から受け継ぐと背景と同じ色になるため、タップした瞬間に文字が消えていました。
検証ツールでこの行のチェックを外すと、文字が戻ります。原因は確定です。
ところが、どこにも書いていない
テーマの開発元に問い合わせると、「この行を削除すれば直ります」との回答。
ところが、どこを探しても見つかりません。
- 外観 → カスタマイズ → 追加CSS
- 子テーマの style.css
- コードスニペット系プラグイン
どこにもないのです。
データベースを全文検索して見つけた
最終手段として、phpMyAdmin でデータベース全体を検索しました。検索語はCSSの近くにあったクラス名 calendar です。
注意: phpMyAdminの「構造」タブにある「フィルタ」はテーブル名の絞り込みです。データの中身を探すときは「検索」タブを使います(私は一度間違えました)。
見つかった場所は wp_postmeta テーブル、つまり 固定ページごとのカスタムCSS欄 でした。
テーマには、ページ単位でCSSやJavaScriptを追加できる機能があります。トップページの月別カレンダー用に、以前の自分がそこへ書いていたのです。
ところがテーマがバージョンアップして、個別ページに書き込んだCSS を非表示にしない仕様になっていたのでした。
ようやく表示方法を見つけました。
.calendar a:link { color: #fff; }
.calendar a:hover { color: #ccc; }
a:visited { color: inherit; } /* ← .monthcalendar を付け忘れ */
前の2行には .calendar が付いているのに、3行目だけinherit。
そのためカレンダーだけでなく、サイト全体のリンク に効いていました。トップページだけで起きたのは、そのページ専用のCSSだったからです。
それにしてもCSSがphpMyAdminで探さないと見つからないという経験は初めてでした。
.calendar a:visited { color: #fff; }
ついでに、同じブロックにあったこの記述も直しました。
/* 修正前:td が単独指定になり、全ページの表に効いてしまう */
.calendarth, td { ... }
/* 修正後 */
.calendar th, .monthcalendar td { ... }
これでメニューの文字は消えなくなりました。めでたし……とはいきませんでした。
原因2:今度はカレンダーが消えた
CSSを直したあと、同じ場所のJavaScriptも少し整えて保存しました。
すると、エラーになってしまいました。
JavaScriptを元に戻しても直りません。キャッシュを消しても、シークレットウィンドウでもダメです。
Consoleを見ると、こんなエラーが出ていました。
Uncaught SyntaxError: Unexpected token ')'
実際にページに出力されているスクリプトを確認して、ようやく理由がわかりました。
// 保存したコード
const display = () => {
for (let num = 0; num < 6; num++) {
// 実際に出力されていたコード
const display = () => {
for (let num = 0; num < 6; num++) {
保存した瞬間に、記号がHTML用の文字に変換されていた のです。
| 書いたもの | 保存後 |
|---|---|
=> | => |
< | < |
&& | && |
' | '(一部だけ) |
`(バッククォート) | 削除 |
さらに if (page <= 0) { … } の < から次の > までがHTMLタグとみなされ、間のコードが丸ごと消えていました。これではJavaScriptとして動くはずがありません。
なぜこうなったのか
今回、JavaScriptはブロックエディターの 「カスタムフィールド」パネル から保存していました。
カスタムフィールドは本来、短い文字や数値を保存するための機能です。セキュリティ上の理由で、< や > を含む値はHTMLとして無害化されることがあります。
CSSは同じ方法で保存しても無事でした。JavaScriptには < や => が多いので、まともに影響を受けたのだと思います。
解決:phpMyAdminで直接書き換える
変換を避けるため、phpMyAdminで該当の値を直接書き換えました。
SELECT meta_id, meta_value
FROM wp_postmeta
WHERE post_id = 119
AND meta_key = '(JS用のキー名)';
表示された行を「編集」し、meta_value を正しいコードに置き換えて「実行」。
これで月タブが復活しました。
注意: phpMyAdminでの直接編集はやり直しがききません。事前にバックアップを取り、書き換えるのは目的の1行だけにしましょう。
今回の教訓
:visitedなどの汎用的なセレクタには、必ず範囲を絞るクラスを付ける。
1行の付け忘れが、何年も後にサイト全体へ影響しました。- CSSやJSをなるべく個別ページに書かない、または「どこに書いたか」を記録しておく。
個別ページに CSS が書けるか書けないかはテーマの仕様によるので、テーマに依存する形になります。
なるべく個別ページには記述しない方が安全だなと思いました。
また書いた場所がわからないと、原因がわかっても直せません。今回はデータベース検索で見つけました。 - カスタムフィールド欄からJavaScriptを保存しない。
見た目は保存できていても、中身が変わっていることがあります。テーマやプラグインの専用の入力欄から保存しましょう。
今後このJavaScriptは、自作プラグインに移してGitで管理する予定です。
「どこに書いたっけ?」を二度と繰り返さないために。
同じように「CSSが原因なのは確実なのに、どこにも見当たらない」「JavaScriptが突然壊れた」と困っている方の参考になれば幸いです。
