Popoverコンポーネントのanchorプロパティについて考えたときのメモ

説明

カスタムブロックやコアブロック向けのプラグインでお世話になるPopoverコンポーネント。表示位置を決定する上で大きく影響するのがanchorプロパティである。フィルターのコールバック関数で対象ブロックに干渉できない場合はどうしたらよいのだろうか。

refプロパティを使える場合

Popoverコンポーネントを使い方は「Popover」が参考になる。その表示位置を指定する際に重要なのがanchorプロパティで、そこには対象のコンポーネントのrefプロパティを使用することが多い。たとえば、ToolbarButtonコンポーネントの下にPopoverコンポーネントを使って何かを表示する場合は次のようなコードになる。

const [ popoverAnchor, setPopoverAnchor ] = useState();
const [ visiblePopover, setVisiblePopover ] = useState( false );

return (
<BlockControls>
	<ToolbarGroup>
		<ToolbarButton
			icon={ "admin-comments" }
			onClick={ ( event ) => { setVisiblePopover( true ); } }
			ref={ setPopoverAnchor }
		/>
	</ToolbarGroup>
</BlockControls>
{ visiblePopover && (
	<Popover
		anchor={ popoverAnchor }
		placement={ "bottom" }
	>
		Popover.
	</Popover>
) }
);

この場合、PopoverコンポーネントのanchorプロパティにはToolbarButtonコンポーネントのrefプロパティに指定したsetPopoverAnchorによって更新されるpopoverAnchorを指定している。コンソールに出力されたpopoverAnchorの内容は次の通りで、button要素となる(一部省略)。

<button type="button" id=":r1r:" data-toolbar-item="true" class="components-button components-toolbar-button has-icon" tabindex="-1">...</button>

refプロパティが使えない場合

上記をふまえて今回困ったのが、editor.BlockEditフィルターのコールバック関数内でボタンをクリックし、編集対象のブロックの右側にPopoverコンポーネントを使って各種設定コンポーネントを配置したいケースである。'editor.BlockEdit'フィルターは次のコードになるが、ここには編集対象のブロックは登場せず、refプロパティを指定することができない。

const myPluginEffectControls = createHigherOrderComponent( ( BlockEdit ) => {

// 省略

	return ( props ) => {
		return (
			<>
				<BlockEdit { ...props } />

// 省略

			</>
		);
	};
}, 'myPluginEffectControls' );

addFilter( 
	'editor.BlockEdit',
	'my-plugin/my-effect',
	myPluginEffectControls
);

よい解決作がないか「ChatGPT」に問い合わせてみたところ、ブロックのクライアントIDを使って要素を取得する方法が提示された。クライアントIDはeditor.BlockEditフィルターのコールバック関数のpropsに含まれていたので、ここではそれを使用する。対象ブロックの要素を見てみると、id属性は'block-'に続いてクライアントIDがあり、data-block属性にもクライアントIDがあった。どちらでも検索できそうなので、ここではid属性を使って要素を取得してみた。

const {
	name,
	attributes,
	setAttributes,
	clientId
} = props;

const [ popoverAnchor, setPopoverAnchor ] = useState( null );
useEffect( () => {
	const element = document.getElementById( `block-${clientId}` );
	console.log( element );
	setPopoverAnchor( element );
}, [ clientId ] );

これでコンソールに出力されたelementの内容は「null」。エディター画面のHTMLソースを見直すと、ブロックエディターの部分がiframe要素になっており、それが原因で対象ブロックの要素が取得できなかったようだ。このことをふまえ、次のように修正した。

useEffect( () => {
	const element = document.querySelector( '[name="editor-canvas"]' ).contentWindow.document.getElementById( `block-${clientId}` );
	console.log( element );
	setPopoverAnchor( element );
}, [ clientId ] );

これでコンソールには対象ブロックの要素が出力されたことを確認できた(一部省略)。

<figure tabindex="0" class="block-editor-block-list__block wp-block is-selected drop-shadow is-resized size-large wp-elements-0 wp-duotone-0 wp-block-image is-hovered" id="block-4d2bb532-055b-4f13-9d84-82d5f279962c" role="document" aria-label="ブロック: 画像" data-block="4d2bb532-055b-4f13-9d84-82d5f279962c" data-type="core/image" data-title="画像">...</figure>

いくつかの環境で試したところ、ブロックエディターの部分がiframe要素になるケースとそうならないケースがあることがわかり、最終的には次のコードに落ち着いた。

useEffect( () => {
	const element = document.querySelector( '[name="editor-canvas"]' ) ?
		document.querySelector( '[name="editor-canvas"]' ).contentWindow.document.getElementById( `block-${clientId}` ) :
		document.getElementById( `block-${clientId}` );
	setPopoverAnchor( element );
}, [ clientId ] );

対象ブロックの要素は無事取得できるようになった。対象ブロックの要素を取得するAPIか、propsにref関連の情報が追加されれば、もっとスッキリする気がしるのだが、あまり需要がないとここままかもしれない。


最終更新 : 2024年08月19日 12:16

お勧め

wp_get_post_terms(2014年7月4日 更新)

mixed wp_get_post_terms( [ int $post_id = 0 [ , string $taxonomy = 'post_tag' [ , array $args = array() ] ] ] )
投稿記事のタクソノミー情報を取得する。

add_action(2023年7月28日 更新)

bool add_action( string $tag, mixed $function_to_add [ , int $priority = 10 [ , int $accepted_args = 1 ] ] )
WordPressシステムの関数にアクション関数を追加する。

get_editor_stylesheets(2014年9月5日 更新)

array get_editor_stylesheets( )
エディタ向けのスタイルシートURLを取得する。

wp_count_terms(2020年12月14日 更新)

array | int | WP_Error wp_count_terms( array | string $args = array() [ , array | string $deprecated = '' ] )
タクソノミーのターム数を取得する。

the_comments_navigation(2018年5月27日 更新)

void the_comments_navigation( [ array $args = array() ] )
コメントナビゲーションを表示する。