2026年8月に追記しました。7.1 が8月19日にリリースされ、iframe 化に付いていた条件がなくなったので、その点を書き直しています。あわせて「データ API に寄せきれないコードをどうするか」を1節足しました。元記事はデータ API へ移す話だけで止まっていて、移せない場合の答えがなかったためです。
【日付を入れる】、自分のプラグインを 7.0 で動かしたら、メタボックスに仕込んでいた JavaScript が急に効かなくなりました。
エラーも出ていません。ただ、いつも掴んでいたはずのエディタの要素が document.querySelector で取れない。null が返ってくる。半年前に書いて、ずっと動いていたコードです。何も変えていないのに、動かなくなった。
先に結果を書くと、原因はエディタが iframe の中に入ったことでした。そしてこの件を追いかけているうちに、add_meta_box で作ってきたメタボックスそのものが、7.0 の新しい編集体験と噛み合わなくなっていることも分かりました。この2つは根が同じで、直し方も同じです。
検証環境:【WordPress のバージョンと確認日を入れる】。最終的な挙動は、公式のフィールドガイドと自分の環境で確認してください。
掴めなくなった理由
7.0 の前まで、ブロックエディタは管理画面と同じページの中で動いていました。だからメタボックスに置いた JavaScript から、エディタの要素を document.querySelector で掴んで、値を読んだり書いたりできた。同じ文書の中にあったからです。
7.0 では、エディタが iframe の中に入ります。iframe は外側のページとは別の文書です。外から document.querySelector でエディタ内の要素を探しても、見つかりません。
// ❌ iframe化で動かなくなる:外側のdocumentからエディタ内を探す
const title = document.querySelector('.editor-post-title__input');
title.value = '...'; // iframe内なので、外側のdocumentからは掴めない
**7.0 の時点では、これは条件付きでした。**投稿に挿入されているブロックの API バージョンが全部 3 以上なら iframe 化し、1つでも古いものがあれば iframe を外して互換性を保つ、という判定が入っていたからです。だから環境によっては、まだ壊れていませんでした。
7.1 で、その判定がなくなりました。テーマがブロックテーマかクラシックテーマかにも、ブロックの API バージョンにも関係なく、常に iframe 化されます。**レガシーなメタボックスを登録しているサイトも例外ではありません。**7.0 のときに「うちはまだ iframe にならないから」で先送りしていた場合、8月19日からその逃げ道はなくなりました。
余談ですが、Gutenberg プラグインの 22.6 以降はプラグインが有効なら強制的に iframe 化されます。手元で先に再現したいなら、これが早いです。
クラシックメタボックスの、もう一つの問題
JS が掴めない件を追ううちに、add_meta_box 自体にも別の問題があると分かりました。
7.0 の目玉は、複数人が同じ投稿を同時に編集する仕組みです。ここに制約があります。クラシックメタボックスがある投稿タイプは、その同時編集が無効になる。Classic ブロック(core/freeform)がある投稿も同様です。
つまり add_meta_box を使い続けると、その投稿タイプだけ新しい編集体験から外れます。動かなくなるわけではありませんが、新機能に乗れない旧道に取り残される。しかも iframe 化で、メタボックス内の JS も見直しが要る。旧来のやり方が、二重に効かなくなっていく感じでした。
移行:register_post_meta に寄せる
対処は、メタボックスでやっていたことを、登録済みメタと REST 公開に移すことです。iframe の件も、メタボックスの件も、これで一度に片づきます。
まず、自分のプラグインの add_meta_box を全部洗い出します。ここが移行の起点です。
grep -rn "add_meta_box" ./
見つかったメタを register_post_meta で登録し直します。肝は show_in_rest => true です。これがあると、ブロックエディタ側から REST 経由でメタを読み書きできるようになります。
add_action('init', function () {
register_post_meta('post', 'myplugin_subtitle', [
'show_in_rest' => true, // これでエディタ(REST)から扱える
'single' => true,
'type' => 'string',
'auth_callback' => function () {
return current_user_can('edit_posts');
},
]);
});
auth_callback を省くと権限チェックが緩くなります。誰がこのメタを書き換えられるかを、ここで明示しておきます。
入力UIは、エディタ側のパネルで出す
メタボックスの HTML フォームの代わりは、エディタのサイドパネルに置きます。ここは JavaScript 側の実装になります。
DOM を直接掴んでいたころと違い、useEntityProp でメタを読み書きします。**iframe の内か外かを気にしなくて済むのが、この形の利点です。**エディタが管理しているデータに、正規の経路で触るからです。冒頭で null が返ってきた原因は、そもそも DOM を掴みにいっていたことでした。
コードは末尾にまとめて置きます。
データAPIに寄せきれないコードをどうするか
ここが今回の追記です。元記事はデータ API に移す話で終わっていましたが、実際には移しきれないコードが残ります。
サードパーティのライブラリを噛ませている、既存の jQuery プラグインをそのまま動かしている、キャンバス内の要素の座標を測っている。こういうものは、useEntityProp では置き換えられません。
その場合の定石が2つ示されています。
ひとつは、**グローバルな document や window を掴むのをやめて、キャンバス内の要素から辿ること。**要素の ownerDocument が、その要素が属する文書です。そこから defaultView を取れば、対応する window が得られます。
// ❌ どの文書か分からないまま掴む
const doc = document;
// ✅ キャンバス内の要素から、その要素が属する文書を辿る
const doc = node.ownerDocument;
const view = doc.defaultView;
もうひとつは、**イベントリスナの登録と解除に useRefEffect を使うこと。**iframe は再マウントされることがあるので、登録しっぱなしにすると、消えた文書にリスナが残ります。ref に紐づけておけば、要素が外れたときに解除まで走ります。
不具合の原因は、ほぼこの1点に集約されると書かれています。**iframe が独自の document と window を持っていて、エディタスクリプトが動く管理画面とは別だから。**グローバルを掴んでいるコードは、全部違う文書を見に行きます。
自分の場合、移行できたものはデータ API に寄せて、どうしても DOM を触る必要が残った箇所だけ ownerDocument 経由に書き換えました。全部をデータ API に寄せる必要はありません。
保存側は、そのままでいい部分もある
register_post_meta で show_in_rest を有効にしておくと、エディタからの保存は REST 経由で処理されます。旧来の save_post で $_POST を拾って update_post_meta していた処理は、エディタ経由の保存では通らなくなることがあります。
ただ、クラシックエディタや外部からの保存経路も残すなら、save_post 側を併存させる判断はありえます。自分は、エディタ経由を REST に寄せつつ、nonce と権限を見たうえで save_post も残す形にしました。ここは、プラグインがどの経路から保存されうるかで変わります。
確認する順番
移行したら、この順で確かめます。
| 確認すること | 見る場所 | 外れていたときの原因 |
|---|---|---|
| メタが REST に出ているか |
/wp-json/wp/v2/posts/{id} の meta
|
show_in_rest が無い |
| パネルで読み書きできるか | エディタのサイドパネル |
auth_callback か登録先の post type |
| 旧JSが残っていないか | grep -rn "document.querySelector" ./ |
iframe 前提の DOM 操作 |
| 残す DOM 操作が文書を辿っているか |
ownerDocument を使っているか |
グローバルな document を掴んでいる |
3行目が、冒頭の自分のミスそのものです。4行目は今回足しました。document.querySelector を消しただけでは足りず、残した DOM 操作が正しい文書を見ているかまで確認が要ります。
コード
エディタのドキュメント設定パネルに、メタの入力欄を足す形です。
import { registerPlugin } from '@wordpress/plugins';
import { PluginDocumentSettingPanel } from '@wordpress/editor';
import { useEntityProp } from '@wordpress/core-data';
import { TextControl } from '@wordpress/components';
const Panel = () => {
const [meta, setMeta] = useEntityProp('postType', 'post', 'meta');
return (
<PluginDocumentSettingPanel title="Myplugin">
<TextControl
label="サブタイトル"
value={ meta.myplugin_subtitle || '' }
onChange={ (v) => setMeta({ ...meta, myplugin_subtitle: v }) }
/>
</PluginDocumentSettingPanel>
);
};
registerPlugin('myplugin-panel', { render: Panel });
PluginDocumentSettingPanel の import 元は @wordpress/editor です。古い記事では @wordpress/edit-post から取っているものがあります。
まとめ
- 7.0 でエディタが iframe 化。7.1 で条件がなくなり、テーマ種別にもブロック API バージョンにも関係なく常に iframe
- 外側の document からエディタ内 DOM は掴めない
- クラシックメタボックス(
add_meta_box)がある投稿タイプは、同時編集が無効になる - 対処は
register_post_meta+show_in_rest => true。iframe 対応とメタボックス問題を一度に解決 - 入力 UI はエディタのパネルへ。
useEntityPropでメタを読み書きする - データ API に寄せきれない DOM 操作は、
ownerDocumentとdefaultViewを辿る。リスナはuseRefEffect - 保存経路は要確認。エディタ経由は REST。
save_postを残すかはプラグイン次第 - 手元で先に再現したいなら、Gutenberg プラグイン 22.6 以降を入れる
7.0 対応で、いちばん腰が重かったのがこの移行でした。長く使ってきた書き方なので。ただ、データを REST に出す形に寄せておくと、iframe 化にも同時編集にも乗れて、結果として身軽になりました。
そして 7.1 からは、腰が重いかどうかに関係なく、iframe は必ず来ます。
ふだんはraplsworks.comで、WordPressプラグイン開発やClaude Codeまわりのことを書いています。