alt が空の画像に、代わりの文を自動で入れる:アイキャッチは記事のタイトル、ウィジェットは表示のときだけ足す

広告WordPress・PHP を学べる本

Amazonのアソシエイトとして、WordPress functions.phpは適格販売により収入を得ています。

このサイトの画像を見直したら、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 が空のときだけ、記事のタイトルを入れます。

functions/auto-alt.php
<?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 というフィルターを通します。

functions/auto-alt.php(続き)
// カスタム 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="" と書いていました。

次の順で、最初に見つかった文を入れるようにしました。

inc/widgets/banner_list.php(直したところ)
// バナーの画像の 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 がありませんでした。

ここは、星の数をそのまま書きました。

page-feedback.php(星 5 つの例)
// 前
'<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="" と書いている所は、代替テキスト → タイトル → リンク先のタイトル、のように順に探して入れる
  • メディアに代替テキストが入っている画像は、そのまま使われる

参考

関連機能

  1. WordPress お問合せフォームの郵便番号に入力したら住所を自動で入力する機能を追加 ( Contact Form 7 )

  2. Smart Custom Fieldsの定義をfunctions.phpで行う

  3. WordPress で本番と作業環境を見分ける(wp_get_environment_type)と、管理者だけに出す判定

サイドバー

よく使うカスタマイズ

最近の記事

Profile

PAGE TOP