この記事では、WordPress の投稿ごとに「そのページ専用の CSS/JS」を記述・適用できるようにする方法を解説します。メタキーは
_custom_css
と _custom_js
を用い、投稿編集画面に専用のメタボックスを追加し、フロントではその投稿ページだけに出力します。Gutenberg の[カスタムフィールド]からも編集できるよう
register_meta()
で REST 公開設定を行い、安全面にも配慮した実装です。
実装の全体像
手順は三つです。まずメタデータを登録してブロックエディタ経由でも扱えるようにします。次に、投稿編集画面に「投稿専用CSS」「投稿専用JS」のテキストエリアを設置します。最後に、フロント側でシングル投稿ページに限ってそれぞれを挿入します。JS はフッターでインライン挿入、CSS は <head>
に <style>
を出力します。
1) メタ登録(_custom_css と _custom_js)
メタキーを登録し、REST 公開と権限チェックを定義します。ここでは管理者・編集者相当の
unfiltered_html
権限を持つユーザーのみ編集できるようにしています。
// functions.php
add_action('init', function () {
register_meta('post', '_custom_css', [
'type' => 'string',
'single' => true,
'show_in_rest' => true,
'auth_callback' => function () {
return current_user_can('unfiltered_html');
},
'sanitize_callback' => null,
]);
register_meta('post', '_custom_js', [
'type' => 'string',
'single' => true,
'show_in_rest' => true,
'auth_callback' => function () {
return current_user_can('unfiltered_html');
},
'sanitize_callback' => null,
]);
});
2) メタボックスの追加(投稿専用CSS/JS)
投稿編集画面にテキストエリアを追加します。<style>
や <script>
タグは不要で、そのまま CSS/JS のみを記述します。保存処理ではタグの除去と制御文字の除去を行います。
// functions.php
add_action('add_meta_boxes', function () {
add_meta_box(
'custom_css_box',
'投稿専用CSS(_custom_css)',
function ($post) {
$val = get_post_meta($post->ID, '_custom_css', true);
wp_nonce_field('save_custom_css_' . $post->ID, 'custom_css_nonce');
echo '<p style="margin:8px 0 4px;">この投稿のみに適用するCSSを記入してください(<code><style></code>タグは不要)。</p>';
echo '<textarea name="_custom_css" style="width:100%;height:260px;font-family:monospace;">'
. esc_textarea($val) . '</textarea>';
},
'post',
'normal',
'default'
);
add_meta_box(
'custom_js_box',
'投稿専用JS(_custom_js)',
function ($post) {
$val = get_post_meta($post->ID, '_custom_js', true);
wp_nonce_field('save_custom_js_' . $post->ID, 'custom_js_nonce');
echo '<p style="margin:8px 0 4px;">この投稿のみに適用するJavaScriptを記入してください(<code><script></code>タグは不要)。</p>';
echo '<textarea name="_custom_js" style="width:100%;height:260px;font-family:monospace;">'
. esc_textarea($val) . '</textarea>';
},
'post',
'normal',
'default'
);
});
// 保存処理(CSS)
add_action('save_post_post', function ($post_id) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (isset($_POST['custom_css_nonce'])
&& wp_verify_nonce($_POST['custom_css_nonce'], 'save_custom_css_' . $post_id)
&& current_user_can('edit_post', $post_id)) {
$raw = isset($_POST['_custom_css']) ? wp_unslash($_POST['_custom_css']) : '';
// <style> タグは除去(インライン出力のため不要)
$raw = preg_replace('#</?style[^>]*>#i', '', $raw);
// 制御文字の除去
$raw = preg_replace('/[^\P{C}\t\n\r]+/u', '', $raw);
update_post_meta($post_id, '_custom_css', $raw);
}
}, 10, 1);
// 保存処理(JS)
add_action('save_post_post', function ($post_id) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (isset($_POST['custom_js_nonce'])
&& wp_verify_nonce($_POST['custom_js_nonce'], 'save_custom_js_' . $post_id)
&& current_user_can('edit_post', $post_id)) {
$raw = isset($_POST['_custom_js']) ? wp_unslash($_POST['_custom_js']) : '';
// <script> タグは除去(インライン出力のため不要)
$raw = preg_replace('#</?script[^>]*>#i', '', $raw);
// 制御文字の除去
$raw = preg_replace('/[^\P{C}\t\n\r]+/u', '', $raw);
update_post_meta($post_id, '_custom_js', $raw);
}
}, 10, 1);
3) フロント出力:_custom_css を head に挿入
シングル投稿ページのみ、<head>
に CSS をインラインで出力します。テーマ側の読み込み後に適用されるようフックの優先度を遅らせています。
// functions.php
add_action('wp_head', function () {
if (!is_singular('post')) return;
$post_id = get_queried_object_id();
if (!$post_id) return;
$css = (string) get_post_meta($post_id, '_custom_css', true);
if ($css === '') return;
echo "<style id=\"post-custom-css\">\n" . $css . "\n</style>";
}, 50);
4) フロント出力:_custom_js をフッターに挿入
JS はフッターでインライン挿入します。ハンドルをダミー登録して wp_add_inline_script()
で差し込みます。AMP 互換テーマをお使いの場合は実行を回避しています。
// functions.php
add_action('wp_enqueue_scripts', function () {
if (!is_singular('post')) return;
$post_id = get_queried_object_id();
if (!$post_id) return;
$js = (string) get_post_meta($post_id, '_custom_js', true);
if ($js === '') return;
// AMP 環境では実行しない
if (function_exists('is_amp_endpoint') && is_amp_endpoint()) return;
// 空のダミーハンドルをフッターに登録して、そこへインラインを差し込む
wp_register_script('post-custom-inline', '', [], null, true);
wp_enqueue_script('post-custom-inline');
// <script> タグは不要。コード本文だけ渡す
wp_add_inline_script('post-custom-inline', $js);
}, 20);
使い方(編集画面と出力の関係)
投稿編集画面の「投稿専用CSS(_custom_css)」と「投稿専用JS(_custom_js)」にそれぞれ記述します。CSS は <head>
に、JS はフッターに出力され、どちらもその投稿ページでのみ適用・実行されます。<style>
や <script>
のタグは不要です。エディタ右サイドバーの[カスタムフィールド]から同メタキーを直接編集することもできます。
安全性と権限設計のポイント
本実装では、任意コードを保存できるのは unfiltered_html
権限を持つユーザーに限定しています。寄稿者や著者ロールなどに開放するのは避けてください。また、保存時に <style>
と <script>
タグを除去し、制御文字も除去しています。フロントではシングル投稿に限定し、AMP 環境では JS を出さないようにしています。
拡張:固定ページ・カスタム投稿タイプにも適用する
投稿以外にも適用したい場合は、出力フックの条件を is_singular()
に変更し、対象の投稿タイプを配列で制御します。以下は「投稿と固定ページと article カスタム投稿タイプ」に適用する例です。
// 対象を配列で管理
function my_custom_code_targets() {
return ['post', 'page', 'article']; // ここに対象の投稿タイプを追加
}
// CSS 出力
add_action('wp_head', function () {
if (!is_singular(my_custom_code_targets())) return;
$post_id = get_queried_object_id();
$css = (string) get_post_meta($post_id, '_custom_css', true);
if ($css !== '') {
echo "<style id=\"post-custom-css\">\n" . $css . "\n</style>";
}
}, 50);
// JS 出力
add_action('wp_enqueue_scripts', function () {
if (!is_singular(my_custom_code_targets())) return;
if (function_exists('is_amp_endpoint') && is_amp_endpoint()) return;
$post_id = get_queried_object_id();
$js = (string) get_post_meta($post_id, '_custom_js', true);
if ($js === '') return;
wp_register_script('post-custom-inline', '', [], null, true);
wp_enqueue_script('post-custom-inline');
wp_add_inline_script('post-custom-inline', $js);
}, 20);
トラブルシューティング
JS が動かない場合はコンソールエラーがないかを確認します。依存ライブラリ(例:jQuery)に頼るコードを書く場合