このサイトの画像を見直したら、alt(代わりの文)が空のまま出ている画像がいくつもありました。
多かったのは、記事の一覧や関連記事に出るアイキャッチです。
メディアの「代替テキスト」を 1 枚ずつ入れるのは大変なので、表示のときに自動で入れる形にしました。
DB の中身は変えません。
alt が空だとどうなるか
alt は、画像が見えない人や、画像が読み込めなかったときに、画像の代わりに伝える文です。
画面の読み上げでは、alt が読まれます。
検索エンジンも、画像の中身を知る手がかりにします。
ただ、飾りだけの画像は、alt=""(空)にしておくのが正しい書き方です。
この記事で直すのは、中身のある画像(記事のアイキャッチ・リンクのバナーなど)で、alt が空になっていたものです。
どこで alt が空だったか
| 場所 | 画像の出し方 | 直し方 |
|---|---|---|
| 記事の一覧・関連記事・サイドの新着のアイキャッチ | WordPress の関数(the_post_thumbnail() など) |
フィルターで、空のときだけ記事のタイトルを入れる |
| サイドの「カスタム HTML」のウィジェット | ウィジェットに書いた HTML | 表示のときだけ、決まった画像に alt を足す |
| バナーのウィジェット(テーマのもの) | テーマのコードで alt="" と書いていた |
代替テキスト → バナーのタイトル → リンク先のタイトル の順で入れる |
| 評価の星の画像 | テーマのコードで alt を書いていなかった | 「評価 5」などを書く |
1. アイキャッチ:wp_get_attachment_image_attributes
アイキャッチを出す the_post_thumbnail() は、中で wp_get_attachment_image() を使っています。
この関数は、<img> の属性を作ったあとに、wp_get_attachment_image_attributes というフィルターを通します。
ここで、alt が空のときだけ、記事のタイトルを入れます。
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
function my_auto_image_alt( $attr, $attachment ) {
// alt が入っていれば、そのまま
if ( ( isset( $attr['alt'] ) && '' !== trim( $attr['alt'] ) ) || ! $attachment ) {
return $attr;
}
$post_id = 0;
$current = get_post();
if ( $current && (int) get_post_thumbnail_id( $current ) === (int) $attachment->ID ) {
// 1. 表示中の記事のアイキャッチなら、その記事
$post_id = $current->ID;
} elseif ( $attachment->post_parent ) {
// 2. 画像の親の記事(その画像を上げた記事)
$post_id = $attachment->post_parent;
} else {
// 3. その画像をアイキャッチにしている記事を 1 件探す
$found = get_posts( array(
'post_type' => 'any',
'posts_per_page' => 1,
'fields' => 'ids',
'meta_key' => '_thumbnail_id',
'meta_value' => $attachment->ID,
'no_found_rows' => true,
) );
$post_id = $found ? $found[0] : 0;
}
if ( $post_id ) {
$title = wp_strip_all_tags( get_the_title( $post_id ) );
if ( '' !== $title ) {
$attr['alt'] = $title;
}
}
return $attr;
}
add_filter( 'wp_get_attachment_image_attributes', 'my_auto_image_alt', 20, 2 );
| 書き方 | 役目 |
|---|---|
wp_get_attachment_image_attributes |
画像の属性(src・alt・class など)を、出す前に変えられるフィルター |
'' !== trim( $attr['alt'] ) |
メディアに代替テキストが入っていれば、何もしないで返す |
get_post() |
今表示している記事(一覧ならループの中の記事) |
get_post_thumbnail_id() |
その記事のアイキャッチの画像の ID。今の画像と同じかを見る |
$attachment->post_parent |
画像を上げたときに開いていた記事(親の記事) |
meta_key '_thumbnail_id' |
その画像をアイキャッチにしている記事を探す |
wp_strip_all_tags() |
タイトルにタグが入っていても、文字だけにする |
優先度 20 |
ほかのフィルター(優先度 10)のあとで動かす |
タイトルを探す順
1 枚の画像が、いくつかの記事で使われていることがあります。
また、ほかの記事を書いているときに上げた画像を、アイキャッチにしていることもあります。
そのため、まず「今表示している記事のアイキャッチか」を見て、そうなら、その記事のタイトルを使います。
記事の外(ウィジェットなど)で出すときは、親の記事、アイキャッチにしている記事、の順に探します。
下の「確かめた結果」で、この順にした理由が数で分かります。
2. カスタム HTML のウィジェット:widget_custom_html_content
サイドのウィジェットに、HTML で画像を書いていて、alt が空になっていました。
ウィジェットの中身を書き換えてもよいのですが、DB は変えずに、表示のときだけ足すことにしました。
カスタム HTML のウィジェットは、中身を出す前に widget_custom_html_content というフィルターを通します。
// カスタム HTML のウィジェットの画像で、alt が空のものに alt を入れる
// ウィジェットの中身(DB)は変えずに、表示のときだけ足す
function my_widget_image_alt( $content ) {
$alts = array(
'/wp-content/themes/(テーマのフォルダ)/lancers.png' => 'ランサーズ',
'/wp-content/themes/(テーマのフォルダ)/rank.png' => 'ランサーズの現在のランク:認定ランサー',
);
foreach ( $alts as $src => $alt ) {
$content = preg_replace(
'#(<img\b[^>]*' . preg_quote( $src, '#' ) . '"[^>]*?)\salt=""#i',
'$1 alt="' . esc_attr( $alt ) . '"',
$content
);
}
return $content;
}
add_filter( 'widget_custom_html_content', 'my_widget_image_alt' );
| 書き方 | 役目 |
|---|---|
widget_custom_html_content |
カスタム HTML のウィジェットの中身を、出す前に変えられるフィルター |
$alts |
画像のファイルと、入れる文の組み合わせ |
preg_quote() |
ファイルの名前の「.」などを、ただの文字として探す |
\salt="" |
同じ <img> の中の、空の alt だけを置き換える |
esc_attr() |
入れる文を、属性に入れても安全な形にする |
決まった画像だけを置き換えるので、ほかの画像や、すでに alt が入っている画像は変わりません。
3. バナーのウィジェット:代替テキスト → タイトル → リンク先
テーマのバナーのウィジェットは、コードの中で alt="" と書いていました。
次の順で、最初に見つかった文を入れるようにしました。
// バナーの画像の alt:画像の代替テキスト → バナーのタイトル → リンク先のページのタイトル
$banner_alt = trim( (string) get_post_meta( $instance['banner_image' . $i], '_wp_attachment_image_alt', true ) );
if ( '' === $banner_alt ) {
$banner_alt = $banner_title;
}
if ( '' === $banner_alt && $banner_url ) {
$banner_link_id = url_to_postid( $banner_url );
$banner_alt = $banner_link_id ? wp_strip_all_tags( get_the_title( $banner_link_id ) ) : '';
}
$output .= '<img width="300" height="140" src="' . esc_attr( $banner_image ) . '" alt="' . esc_attr( $banner_alt ) . '">';
| 順 | 使う文 | 書き方 |
|---|---|---|
| 1 | メディアの「代替テキスト」 | get_post_meta( ID, '_wp_attachment_image_alt', true ) |
| 2 | ウィジェットで入れたバナーのタイトル | $banner_title |
| 3 | リンク先が、このサイトの記事や固定ページなら、そのタイトル | url_to_postid() で記事の ID にしてから get_the_title() |
どれもないとき(外のサイトへのリンクで、タイトルもないとき)は、前と同じ空の alt のままです。
4. テーマに直接書いた画像:評価の星
評価の星の画像は、テーマのコードで <img> を書いていて、alt がありませんでした。
ここは、星の数をそのまま書きました。
// 前 '<img src="/wp-content/themes/(テーマのフォルダ)/assets/images/star5.png">' // 後 '<img src="/wp-content/themes/(テーマのフォルダ)/assets/images/star5.png" alt="評価 5">'
確かめた結果
このサイトの作業環境(PHP 8.3.32)で、WordPress を読み込む検証用スクリプトを動かして数えました。
DB には書いていません。
アイキャッチ:フィルターがあるとき・ないとき
アイキャッチのある公開中の記事 74 件で、アイキャッチを wp_get_attachment_image() で出し、alt を調べました。
フィルターがないときは、remove_filter() で外して出しました。
| フィルターなし | フィルターあり(記事のループの中) | フィルターあり(ループの外) | |
|---|---|---|---|
| 出した画像 | 74 | 74 | 74 |
| alt が空 | 16 | 0 | 0 |
| その記事のタイトルが入った | 0 | 16 | 14 |
| ほかの記事のタイトルが入った | 0 | 0 | 2 |
| メディアの代替テキストのまま | 58 | 58 | 58 |
メディアに代替テキストが入っている 58 枚は、フィルターがあってもなくても、代替テキストのままでした(58 枚とも、メディアの値と同じ)。
ループの外で出した 2 枚は、ほかの記事を書いているときに上げた画像で、親の記事のタイトルが入りました。
記事の一覧のように、ループの中で出すときは、「今表示している記事」を先に見るので、16 枚ともその記事のタイトルになりました。
カスタム HTML のウィジェット
試しの文字列をフィルターに通して、前と後を比べました。
<a href="#"><img src="/wp-content/themes/(テーマのフォルダ)/lancers.png" width="120" alt=""></a> <img src="/wp-content/themes/(テーマのフォルダ)/rank.png" alt=""> <img src="/wp-content/themes/(テーマのフォルダ)/rank.png" alt="すでに入っている文"> <img src="/wp-content/uploads/other.png" alt="">
<a href="#"><img src="/wp-content/themes/(テーマのフォルダ)/lancers.png" width="120" alt="ランサーズ"></a> <img src="/wp-content/themes/(テーマのフォルダ)/rank.png" alt="ランサーズの現在のランク:認定ランサー"> <img src="/wp-content/themes/(テーマのフォルダ)/rank.png" alt="すでに入っている文"> <img src="/wp-content/uploads/other.png" alt="">
決まった 2 つの画像の空の alt だけが入り、すでに alt がある画像と、ほかの画像はそのままでした。
作業環境のカスタム HTML のウィジェット 9 個の中身でも数えました。
| フィルターなし | フィルターあり | |
|---|---|---|
alt="" の画像 |
6 | 0 |
ウィジェットの中身(DB)は変わっていません。
バナーのウィジェット
作業環境のバナー 4 枚は、どれもメディアに代替テキストが入っていて、その文が使われていました。
2 と 3 の順を確かめるため、検証用スクリプトの中だけで、バナーの設定を作ってウィジェットを出しました(DB には保存していません)。
| 試した設定 | 入った alt |
|---|---|
| 代替テキストのある画像・タイトルあり | メディアの代替テキスト |
| 代替テキストのない画像・タイトルあり | バナーのタイトル |
| 代替テキストのない画像・タイトルなし・リンク先はこのサイトの記事 | リンク先の記事のタイトル |
| 代替テキストのない画像・タイトルなし・リンク先は外のサイト | 空のまま |
画面で見た
作業環境のトップのページを開いて、<img> を数えました。
| ページ | 画像 | alt="" |
alt なし |
|---|---|---|---|
| トップ | 51 | 0 | 0 |
| 評価のページ | 19 | 1 | 0 |
評価のページの 1 枚は、本文の中に画像のブロックで入れた画像でした。
本文の中の画像は、この記事のフィルターでは変わりません(下の「気をつけること」)。
評価の星の画像は、作業環境の評価のページには出ていなかったので、画面では確かめていません。
気をつけること
- 本文の中に直接入っている画像(ブロックエディターの画像など)には、
wp_get_attachment_image_attributesは効きません。本文の画像は、編集画面で代替テキストを入れます - 表示のときだけ足すので、メディアの一覧では代替テキストは空のままです。大事な画像は、メディアに代替テキストを入れておくのがいちばん確かです
- 記事のタイトルは、画像の説明とは少し違います。アイキャッチは記事へのリンクとして出ることが多いので、タイトルを使いました
- 飾りだけの画像まで、むりに文を入れる必要はありません
まとめ
- アイキャッチの alt は、
wp_get_attachment_image_attributesのフィルターで、空のときだけ記事のタイトルを入れられる - タイトルは、表示中の記事 → 画像の親の記事 → アイキャッチにしている記事、の順に探すと、ずれにくい
- カスタム HTML のウィジェットは、
widget_custom_html_contentで、DB を変えずに表示のときだけ足せる - テーマで
alt=""と書いている所は、代替テキスト → タイトル → リンク先のタイトル、のように順に探して入れる - メディアに代替テキストが入っている画像は、そのまま使われる


