自社サイトの制作実績ページを点検したら、掲載していた24件すべてが、検索エンジンから辿り着けない状態になっていました。カード自体はクリックできます。マウスで押せば詳細ページへ飛びます。それなのに、リンクとしては1本も存在していませんでした。
原因は、カードを<a>タグではなく、JavaScriptのクリック処理で動かしていたことでした。人間の操作では問題なく動くので、見ただけでは気づけません。修正自体は3箇所で終わりましたが、そこに辿り着くまでに一度誤診をして、無関係なファイルを直して本番に上げ、また戻すという往復をしています。その過程も含めて書きます。
きっかけは、実績を1件追加したことだった
新しい制作実績を1件追加し、一覧ページでの見え方を確認していたときのことです。カードのサムネイル画像の比率を測ろうとしてブラウザの検証ツールでDOMを覗いたところ、カードの中に<a>タグが1つも見当たりませんでした。
最初は「リンクが切れている」と判断しました。これが誤診でした。実際にはカードにdata-permalinkという属性が付いていて、テーマのJavaScriptがクリックを拾い、その値へ画面遷移させていたのです。つまり利用者にとっては正常に動いていました。
問題は別のところにありました。検索エンジンのクローラーはJavaScriptのクリックイベントを人間のようには辿りません。hrefのあるリンクを辿ってページを発見します。つまり実績の詳細ページは、一覧からの内部リンクが1本もない、孤立したページとして置かれていたことになります。
最初に、作業する場所を3つに分けて整理する
この手の作業でいちばん迷うのは「いま、どこを触っているのか」です。先に整理しておきます。
- ブラウザの検証コンソール(調べる場所)。現状の把握と、直した後の確認に使います。ファイルは変わりません
- テーマファイル(直す場所)。一覧ページを描画しているPHPテンプレートです。ここだけを編集します
- WordPressの管理画面(今回は触らない)。記事や実績の中身の問題ではないので、投稿画面をいくら見ても原因は出てきません
用語は3つだけ覚えれば足りる
- 内部リンク:同じサイト内のページ同士をつなぐリンク。クローラーがページを見つける通り道になります
- アンカーテキスト:リンクとして表示されている文字。ここに何が書いてあるかで、リンク先の内容が伝わります
- stretched link:小さなリンクの当たり判定を、擬似要素でカード全面まで広げる書き方。見た目を変えずにカード全体を押せるようにできます
手順0:自分のサイトが該当するか調べる
一覧ページのHTMLに、詳細ページへのリンクが何本あるかを数えます。ターミナルで次を実行してください。ドメインと一覧のパスは自分のものに置き換えます。
curl -s https://example.com/works/ | grep -c 'href="https://example.com/works/'
カードの枚数とだいたい同じ数が返れば正常です。0が返ったら、この記事と同じ状態です。ヘッダーやパンくずの分で数本多く出ることはありますが、判断は「0かどうか」で足ります。
手順1:カードが本当にリンクなのかを判定する【ブラウザの検証コンソール】
一覧ページを開き、検証ツールのコンソールで実行します。カードのクラス名は自分のテーマのものに置き換えてください。
const cards = [...document.querySelectorAll('.work-card')];
console.log('カード枚数:', cards.length);
console.log('リンクを持つカード:', cards.filter(c => c.querySelector('a[href]')).length);
console.log('data属性で動かしているか:', cards.filter(c => c.dataset.permalink).length);
3行目が効いてきます。リンクが0でdata属性が枚数分あれば、JavaScript頼みの構成だと確定します。私の場合はカード24枚、リンク0、data属性24でした。「押せば動く」ことは、リンクがあることの証明になりません。
手順2:どのファイルが描画しているかを特定する【テーマファイル】
ここで私は失敗しています。カードのHTMLを出力している関数をfunctions.phpの中に見つけたので、そこを修正して本番に反映しました。ところが表示は何も変わりませんでした。
原因は単純で、その関数はファイル内に定義されているだけで、どこからも呼ばれていなかったからです。過去の作り替えで使われなくなったまま残っていた、いわゆる死んだコードでした。実際に一覧を描画していたのは、固定ページ用の別テンプレートのほうです。無駄な往復を1回して、変更を本番ごと元に戻しました。
同じ轍を踏まないために、直す前にテーマ全体を検索して、そのHTMLを出しているのがどのファイルかを先に確定させてください。
grep -rn "work-card" --include="*.php" .
複数のファイルが引っかかったら、定義しているだけのものと、実際にループの中で出力しているものを見分けます。関数として定義されている場合は、その関数名でもう一度検索して、呼び出し元があるかどうかを確認するのが確実です。
手順3:タイトルを実リンクにする【テーマファイル】
カード全体を<a>で囲む方法もありますが、既存のCSSがdisplay: flexなどを前提にしているとレイアウトが崩れます。崩さずに済ませるなら、タイトルだけをリンクにするのが安全です。アンカーテキストが実績名になるので、リンクの意味もはっきりします。
<div class="work-title">
<a class="work-link" href="<?php echo esc_url( get_permalink( $id ) ); ?>"><?php echo get_the_title( $id ); ?></a>
</div>
手順4:カード全面をリンクにする【テーマファイル / CSS】
タイトルだけがリンクだと、これまでカードのどこでも押せていたのに操作感が落ちます。擬似要素でリンクの当たり判定をカード全面に広げます。追加するのは実質3行です。
.work-card { position: relative; }
.work-title .work-link { color: inherit; text-decoration: none; }
.work-title .work-link::after { content: ""; position: absolute; inset: 0; z-index: 2; }
1行目が要です。::afterはposition: absoluteで広がるので、基準になる親にposition: relativeが要ります。これが無いと、もっと外側の要素を基準にして画面全体を覆ってしまいます。
もうひとつ確認が要るのが重なり順です。カードの内側にpositionが指定された要素があると、擬似要素より前面に出てクリックを奪うことがあります。私のケースではサムネイル部分にposition: relativeが付いていましたが、z-indexはautoでした。擬似要素側にz-index: 2を与えているので、擬似要素が前面に来ます。次のコードで確認できます。
const card = document.querySelector('.work-card');
[...card.querySelectorAll('*')]
.map(e => ({ cls: e.className, pos: getComputedStyle(e).position, z: getComputedStyle(e).zIndex }))
.filter(o => o.pos !== 'static')
.forEach(o => console.log(o));
z-indexが2以上の要素が出てきたら、擬似要素側の値をそれより大きくします。
既存のJavaScriptは消さなくてよい
カードをクリックで飛ばしていたJavaScriptは、消さずに残して問題ありません。この種の実装は、たいてい次のような分岐を持っています。
if (e.target.closest('a, button')) return;
リンクやボタンの上でのクリックは処理せず、ブラウザ本来の動きに任せる、という書き方です。擬似要素でカード全面を覆うと、クリック対象はリンクになるので、JavaScript側は何もせずに抜けます。二重に遷移することはありません。もし自分のテーマにこの分岐が無ければ、追記しておくと安全です。
つまずき早見表
| 症状 | 原因 | 対処 |
|---|---|---|
| 直したのに表示が変わらない | 編集したファイルが使われていない(死んだコード) | grepで出力元を特定し直す。関数なら呼び出し元の有無を確認 |
| クリック範囲が画面全体になった | カードにposition: relativeが無い | カードの基準を先に作る |
| サムネイルの上だけ押せない | 内側の要素のz-indexが擬似要素より大きい | 擬似要素側のz-indexを上げる |
| カードのレイアウトが崩れた | カード全体を<a>で囲んだ | タイトルだけをリンクにし、擬似要素で広げる方式に変える |
| タイトルの色が変わった・下線が出た | リンクの既定スタイル | color: inheritとtext-decoration: noneを当てる |
直ったことの確認方法
手順0と同じコマンドをもう一度実行します。0だった数字がカード枚数になっていれば成功です。あわせて次の3点を見ます。
- リンク先がすべて詳細ページか。誤って問い合わせページなどに向いていないか、出力されたURLを一覧で確認します
- 中クリックで新しいタブに開けるか。JavaScript頼みだとこれができません。実リンクになった証拠になります
- 見た目が変わっていないか。カードの寸法とカラム数を検証ツールで測り、修正前と一致することを確かめます
そのうえで、Search Consoleの「URL検査」で詳細ページを1件かけてみてください。インデックス状況と、そのページが発見された経路を確認できます。
正直に書いておくこと
この修正で検索順位が上がるとは言えません。内部リンクは評価に関わる要素のひとつですが、それ単体で順位が動くものではありません。ここで直せるのは「クローラーがそのページに辿り着く道を作る」ところまでです。詳細ページの中身が薄ければ、道ができても評価はされません。
また、JavaScriptでのクリック遷移そのものが悪いわけでもありません。近年の検索エンジンはJavaScriptを実行してページを読みます。ただし実行されるかどうかはクローラーの都合に依存しますし、hrefがあるリンクのほうが確実です。中クリックや「新しいタブで開く」が使えるという、利用者側の利点もあります。確実な側に寄せておく、という判断です。
効果の検証にも時間がかかります。クローラーが再訪して構造の変化を認識するまで、数日から数週間の幅があります。直した翌日に何かが変わることは期待しないでください。
要点
- カードが「押せる」ことと「リンクがある」ことは別。
hrefの数を数えれば1行で判定できる - 直す前に、そのHTMLを出力しているファイルを特定する。使われていないコードを直しても表示は変わらない
- タイトルをリンクにして擬似要素で全面に広げれば、見た目も既存のJavaScriptも壊さずに済む