WordPressブロックエディターで、ショートカットキーからブロックの種類を変更するスニペット

はじめに

WordPressのブロックエディターでは、段落、見出し、リスト、引用、コードなど、ブロックの種類をあとから変更できます。

通常は、ブロックを選択して、ツールバーからブロック変換メニューを開き、変更したいブロックを選びます。

ただ、記事作成中に何度もこの操作を行う場合、毎回クリックして変更するのは少し手間です。

そこで今回は、ショートカットキーを使って、選択中のブロックをすぐに別のブロック種類へ変更できるWordPressスニペットを紹介します。

Code Snippetsやfunctions.phpに追加するだけで使えます。


このスニペットでできること

このスニペットを追加すると、ブロックエディター上で次のような操作ができます。

ショートカットキー変更後のブロック
Ctrl + Alt + P段落
Ctrl + Alt + H見出し H2
Ctrl + Alt + 1見出し H1
Ctrl + Alt + 2見出し H2
Ctrl + Alt + 3見出し H3
Ctrl + Alt + 4見出し H4
Ctrl + Alt + 5見出し H5
Ctrl + Alt + 6見出し H6
Ctrl + Alt + Lリスト
Ctrl + Alt + Q引用
Ctrl + Alt + Cコード
Ctrl + Alt + F整形済みテキスト
Ctrl + Alt + Gグループ

投稿や固定ページの編集画面で、対象ブロックを選択した状態でキーを押すと、そのブロックの種類を切り替えられます。


完成コード

以下をそのまま追加してください。

Code Snippetsを使う場合は、「PHPスニペット」として追加します。

<?php
add_action( 'enqueue_block_editor_assets', function () {
    wp_register_script(
        'eg-block-type-shortcuts',
        false,
        array( 'wp-dom-ready', 'wp-data', 'wp-blocks', 'wp-block-editor' ),
        '1.0.0',
        true
    );

    wp_enqueue_script( 'eg-block-type-shortcuts' );

    $script = <<<'JS'
wp.domReady(function () {
    const getSelector = function () {
        return wp.data.select('core/block-editor');
    };

    const getDispatcher = function () {
        return wp.data.dispatch('core/block-editor');
    };

    const shortcuts = {
        p: { action: 'switch', blockName: 'core/paragraph' },
        h: { action: 'switch', blockName: 'core/heading', attributes: { level: 2 } },
        q: { action: 'switch', blockName: 'core/quote' },
        l: { action: 'switch', blockName: 'core/list' },
        c: { action: 'switch', blockName: 'core/code' },
        f: { action: 'switch', blockName: 'core/preformatted' },
        g: { action: 'group' },
        '1': { action: 'switch', blockName: 'core/heading', attributes: { level: 1 } },
        '2': { action: 'switch', blockName: 'core/heading', attributes: { level: 2 } },
        '3': { action: 'switch', blockName: 'core/heading', attributes: { level: 3 } },
        '4': { action: 'switch', blockName: 'core/heading', attributes: { level: 4 } },
        '5': { action: 'switch', blockName: 'core/heading', attributes: { level: 5 } },
        '6': { action: 'switch', blockName: 'core/heading', attributes: { level: 6 } }
    };

    function getTargetBlocks() {
        const selector = getSelector();
        let clientIds = [];
        let blocks = [];

        if (typeof selector.getMultiSelectedBlockClientIds === 'function') {
            const multiClientIds = selector.getMultiSelectedBlockClientIds();

            if (multiClientIds && multiClientIds.length) {
                clientIds = multiClientIds;
                blocks = selector.getMultiSelectedBlocks();
                return { clientIds: clientIds, blocks: blocks };
            }
        }

        const selectedBlock = selector.getSelectedBlock();

        if (selectedBlock && selectedBlock.clientId) {
            clientIds = [selectedBlock.clientId];
            blocks = [selectedBlock];
        }

        return { clientIds: clientIds, blocks: blocks };
    }

    function stripHtml(html) {
        if (!html || typeof html !== 'string') {
            return '';
        }

        const div = document.createElement('div');
        div.innerHTML = html;

        return div.textContent || div.innerText || '';
    }

    function getBlockText(block) {
        if (!block) {
            return '';
        }

        const attributes = block.attributes || {};
        const keys = ['content', 'value', 'text', 'citation'];
        let text = '';

        keys.forEach(function (key) {
            if (!text && typeof attributes[key] === 'string') {
                text = attributes[key];
            }
        });

        if (!text && block.innerBlocks && block.innerBlocks.length) {
            text = block.innerBlocks.map(getBlockText).filter(Boolean).join('\n');
        }

        return stripHtml(text);
    }

    function createFallbackBlock(blockName, blocks, attributes) {
        const text = blocks.map(getBlockText).filter(Boolean).join('\n');

        if (blockName === 'core/paragraph') {
            return wp.blocks.createBlock('core/paragraph', { content: text });
        }

        if (blockName === 'core/heading') {
            return wp.blocks.createBlock('core/heading', {
                content: text,
                level: attributes && attributes.level ? attributes.level : 2
            });
        }

        if (blockName === 'core/quote') {
            return wp.blocks.createBlock('core/quote', { value: text });
        }

        if (blockName === 'core/code') {
            return wp.blocks.createBlock('core/code', { content: text });
        }

        if (blockName === 'core/preformatted') {
            return wp.blocks.createBlock('core/preformatted', { content: text });
        }

        if (blockName === 'core/list') {
            const lines = text.split(/\r?\n/).map(function (line) {
                return line.trim();
            }).filter(Boolean);

            const listItems = lines.map(function (line) {
                return wp.blocks.createBlock('core/list-item', { content: line });
            });

            return wp.blocks.createBlock('core/list', {}, listItems);
        }

        return null;
    }

    function switchBlockType(command) {
        const target = getTargetBlocks();

        if (!target.clientIds.length || !target.blocks.length) {
            return;
        }

        if (!wp.blocks.getBlockType(command.blockName)) {
            return;
        }

        let replacements = null;

        try {
            replacements = wp.blocks.switchToBlockType(target.blocks, command.blockName);
        } catch (error) {
            replacements = null;
        }

        if (!replacements || !replacements.length) {
            const fallback = createFallbackBlock(command.blockName, target.blocks, command.attributes || {});
            replacements = fallback ? [fallback] : null;
        }

        if (!replacements || !replacements.length) {
            return;
        }

        if (command.attributes) {
            replacements = replacements.map(function (block) {
                return wp.blocks.cloneBlock(block, command.attributes);
            });
        }

        getDispatcher().replaceBlocks(target.clientIds, replacements, 0, null);
    }

    function groupBlocks() {
        const target = getTargetBlocks();

        if (!target.clientIds.length || !target.blocks.length) {
            return;
        }

        const innerBlocks = target.blocks.map(function (block) {
            return wp.blocks.cloneBlock(block);
        });

        const groupBlock = wp.blocks.createBlock('core/group', {}, innerBlocks);

        getDispatcher().replaceBlocks(target.clientIds, groupBlock, 0, null);
    }

    document.addEventListener('keydown', function (event) {
        if (event.isComposing) {
            return;
        }

        if (!event.ctrlKey || !event.altKey || event.metaKey) {
            return;
        }

        const key = String(event.key).toLowerCase();
        const command = shortcuts[key];

        if (!command) {
            return;
        }

        const target = getTargetBlocks();

        if (!target.clientIds.length || !target.blocks.length) {
            return;
        }

        event.preventDefault();
        event.stopPropagation();

        if (command.action === 'group') {
            groupBlocks();
            return;
        }

        switchBlockType(command);
    }, true);
});
JS;

    wp_add_inline_script( 'eg-block-type-shortcuts', $script );
} );

追加する場所

方法1 Code Snippetsに追加する

一番おすすめの方法です。

WordPress管理画面から、Code Snippetsを開きます。

新規追加を選びます。

スニペット名は、たとえば次のようにします。

ブロック種類変更ショートカット

コード欄に、先ほどのPHPコードをそのまま貼り付けます。

実行場所は、管理画面側でも読み込まれる設定にしてください。

保存して有効化します。


方法2 functions.phpに追加する

子テーマを使っている場合は、子テーマのfunctions.phpに追加しても使えます。

ただし、親テーマのfunctions.phpに直接追加すると、テーマ更新時に消える可能性があります。

そのため、基本的にはCode Snippetsを使う方が安全です。


使い方

手順1 投稿または固定ページの編集画面を開く

WordPress管理画面から、投稿または固定ページの編集画面を開きます。

このスニペットは、ブロックエディター上で動きます。

クラシックエディターでは動きません。


手順2 変更したいブロックを選択する

変更したいブロックをクリックします。

たとえば、段落ブロックを見出しに変えたい場合は、その段落ブロックを選択します。

複数のブロックを選択した状態でも動作します。


手順3 ショートカットキーを押す

たとえば、選択中のブロックをH2見出しにしたい場合は、次のキーを押します。

Ctrl + Alt + H

または、H3見出しにしたい場合は次のキーを押します。

Ctrl + Alt + 3

選択中のブロックが、指定したブロック種類に変換されます。


ショートカットキーの考え方

このスニペットでは、Ctrl + Altを基本の組み合わせにしています。

理由は、WordPress本体やブラウザ標準のショートカットキーと衝突しにくくするためです。

たとえば、Ctrl + Bは太字、Ctrl + Iは斜体、Ctrl + Zは戻る、Ctrl + Sは保存など、すでに使われているショートカットキーがあります。

そのため、単純なCtrlキーだけのショートカットではなく、Ctrl + Alt + 任意のキーという形にしています。


各ショートカットの役割

Ctrl + Alt + P

選択中のブロックを段落ブロックに変更します。

文章本文に戻したいときに使います。

見出しや引用にしていたブロックを、通常の文章に戻したい場面で便利です。


Ctrl + Alt + H

選択中のブロックをH2見出しに変更します。

ブログ記事では、H2見出しを使う場面が多いため、HキーはH2に割り当てています。

HはHeadingの頭文字です。


Ctrl + Alt + 1から6

選択中のブロックを、H1からH6の見出しに変更します。

たとえば、H3にしたい場合は次のキーです。

Ctrl + Alt + 3

記事本文では、基本的にH2とH3を使うことが多いです。

H1は記事タイトルで使われることが多いため、本文中では多用しない方が安全です。


Ctrl + Alt + L

選択中のブロックをリストブロックに変更します。

複数行の文章を選択している場合、行ごとにリスト項目へ変換します。

手順、特徴、比較項目などを整理したいときに便利です。


Ctrl + Alt + Q

選択中のブロックを引用ブロックに変更します。

引用文、補足、強調したい文章などを別枠で見せたいときに使えます。


Ctrl + Alt + C

選択中のブロックをコードブロックに変更します。

HTML、CSS、PHP、JavaScriptなどのコードを記事内に載せたいときに使います。


Ctrl + Alt + F

選択中のブロックを整形済みテキストに変更します。

コードブロックほどではないものの、改行やスペースをなるべく保ったまま表示したい場合に使います。


Ctrl + Alt + G

選択中のブロックをグループブロックで囲みます。

複数のブロックをまとめて、背景色、余白、枠線などを付けたい場合に便利です。


コードの仕組みをひとつずつ解説

1. ブロックエディターだけで読み込む

最初の部分では、ブロックエディター用のJavaScriptを読み込んでいます。

add_action( 'enqueue_block_editor_assets', function () {

enqueue_block_editor_assets は、WordPressのブロックエディター画面でスクリプトやCSSを読み込むためのフックです。

このフックを使うことで、公開ページではなく、投稿編集画面や固定ページ編集画面でだけ処理を動かせます。


2. インラインJavaScriptを登録する

次の部分で、JavaScriptを登録しています。

wp_register_script(
'eg-block-type-shortcuts',
false,
array( 'wp-dom-ready', 'wp-data', 'wp-blocks', 'wp-block-editor' ),
'1.0.0',
true
);

ここでは、外部JSファイルを読み込むのではなく、PHP内に直接JavaScriptを書き込む形にしています。

そのため、ファイルを別で作成する必要がありません。

Code Snippetsだけで完結できます。


3. WordPressのブロックAPIを使う

このスニペットでは、WordPressのブロックエディターが持っている機能を使っています。

主に使っているのは、次の3つです。

処理内容
wp.data.select現在選択中のブロック情報を取得する
wp.data.dispatchブロックを差し替える処理を実行する
wp.blocksブロックを作成、複製、変換する

直接DOMを書き換えるのではなく、WordPressのブロックエディター内部の仕組みに沿って処理しています。

そのため、単純にHTMLだけを書き換える方法よりも安定しやすいです。


4. ショートカット一覧を定義する

次の部分で、どのキーを押したときに、どのブロックへ変更するかを決めています。

const shortcuts = {
p: { action: 'switch', blockName: 'core/paragraph' },
h: { action: 'switch', blockName: 'core/heading', attributes: { level: 2 } },
q: { action: 'switch', blockName: 'core/quote' },
l: { action: 'switch', blockName: 'core/list' },
c: { action: 'switch', blockName: 'core/code' },
f: { action: 'switch', blockName: 'core/preformatted' },
g: { action: 'group' },
'1': { action: 'switch', blockName: 'core/heading', attributes: { level: 1 } },
'2': { action: 'switch', blockName: 'core/heading', attributes: { level: 2 } },
'3': { action: 'switch', blockName: 'core/heading', attributes: { level: 3 } },
'4': { action: 'switch', blockName: 'core/heading', attributes: { level: 4 } },
'5': { action: 'switch', blockName: 'core/heading', attributes: { level: 5 } },
'6': { action: 'switch', blockName: 'core/heading', attributes: { level: 6 } }
};

たとえば、p は段落ブロックです。

p: { action: 'switch', blockName: 'core/paragraph' }

h は見出しブロックです。

h: { action: 'switch', blockName: 'core/heading', attributes: { level: 2 } }

attributes: { level: 2 } を指定しているため、H2見出しになります。


5. 選択中のブロックを取得する

次の関数で、現在選択されているブロックを取得しています。

function getTargetBlocks() {

単体選択だけでなく、複数選択にも対応しています。

複数ブロックが選択されている場合は、複数ブロックをまとめて取得します。

const multiClientIds = selector.getMultiSelectedBlockClientIds();

単体ブロックだけが選択されている場合は、こちらで取得します。

const selectedBlock = selector.getSelectedBlock();

この処理があることで、1つのブロックだけでなく、複数ブロックをまとめて変換できます。


6. HTMLタグを取り除いてテキストを取り出す

ブロックの内容には、HTMLタグが含まれている場合があります。

そのまま別のブロックに入れると、不要なタグが残ることがあります。

そのため、次の関数でHTMLをテキストに変換しています。

function stripHtml(html) {

内部では、一度div要素にHTMLを入れ、textContentで文字だけを取り出しています。

const div = document.createElement('div');
div.innerHTML = html;

return div.textContent || div.innerText || '';

これにより、ブロック変換時に文章だけを取り出せます。


7. ブロック内の文字を取得する

次の関数では、ブロックの中にある文章を取得しています。

function getBlockText(block) {

段落ブロック、見出しブロック、引用ブロックなどは、本文の保存先が少しずつ異なります。

そのため、次のように複数の属性を確認しています。

const keys = ['content', 'value', 'text', 'citation'];

たとえば、段落や見出しではcontentが使われることが多いです。

引用ブロックではvaluecitationが関係する場合があります。

このように複数の候補を見て、できるだけ文字を取り出せるようにしています。


8. 通常の変換ができない場合の予備処理

WordPressには、ブロックを別のブロック種類へ変換するための機能があります。

このスニペットでは、まず次の処理を使います。

wp.blocks.switchToBlockType(target.blocks, command.blockName);

ただし、すべてのブロックが必ずきれいに変換できるわけではありません。

そこで、変換できなかった場合に備えて、予備の変換処理を用意しています。

function createFallbackBlock(blockName, blocks, attributes) {

たとえば、段落に変換する場合は、取得した文字を段落ブロックに入れ直します。

return wp.blocks.createBlock('core/paragraph', { content: text });

見出しに変換する場合は、見出しブロックを新しく作成します。

return wp.blocks.createBlock('core/heading', {
content: text,
level: attributes && attributes.level ? attributes.level : 2
});

この予備処理があるため、通常変換で失敗しても、できるだけ内容を残したまま変換できます。


9. リストブロックへの変換

リストブロックの場合は、少し特殊です。

文章をそのまま1つのリストにするのではなく、改行ごとにリスト項目へ分けています。

const lines = text.split(/\r?\n/).map(function (line) {
return line.trim();
}).filter(Boolean);

その後、各行をリスト項目にしています。

const listItems = lines.map(function (line) {
return wp.blocks.createBlock('core/list-item', { content: line });
});

最後に、それらをリストブロックの中に入れています。

return wp.blocks.createBlock('core/list', {}, listItems);

これにより、複数行の文章を選択してリスト化したときに、行ごとのリストになりやすくなります。


10. ブロックを差し替える

実際にブロックを変更しているのは、次の処理です。

getDispatcher().replaceBlocks(target.clientIds, replacements, 0, null);

replaceBlocks は、現在選択されているブロックを、新しいブロックに差し替えるための処理です。

ここで、段落、見出し、リスト、引用、コードなどのブロックへ変更しています。


11. グループ化の処理

グループ化だけは、単純なブロック変換とは少し違います。

既存のブロックを別の種類に変えるのではなく、選択中のブロックをグループブロックの中に入れます。

const groupBlock = wp.blocks.createBlock('core/group', {}, innerBlocks);

そして、元のブロックをグループブロックに差し替えています。

getDispatcher().replaceBlocks(target.clientIds, groupBlock, 0, null);

これにより、選択中のブロックをまとめてグループ化できます。


12. キー入力を監視する

最後に、キーボード操作を監視しています。

document.addEventListener('keydown', function (event) {

ここで、CtrlキーとAltキーが押されているかを確認しています。

if (!event.ctrlKey || !event.altKey || event.metaKey) {
return;
}

Ctrl + Altが押されていない場合は、何もしません。

次に、押されたキーを取得します。

const key = String(event.key).toLowerCase();

そのキーがショートカット一覧に登録されていれば、処理を実行します。

const command = shortcuts[key];

ショートカットキーを変更したい場合

ショートカットキーを変更したい場合は、次の部分を編集します。

const shortcuts = {

たとえば、H2見出しのショートカットをhではなくmにしたい場合は、次のように変更します。

変更前です。

h: { action: 'switch', blockName: 'core/heading', attributes: { level: 2 } },

変更後です。

m: { action: 'switch', blockName: 'core/heading', attributes: { level: 2 } },

この場合、ショートカットキーは次のようになります。

Ctrl + Alt + M

H2ではなくH3をメインにしたい場合

Ctrl + Alt + HでH2ではなくH3にしたい場合は、次の部分を変更します。

変更前です。

h: { action: 'switch', blockName: 'core/heading', attributes: { level: 2 } },

変更後です。

h: { action: 'switch', blockName: 'core/heading', attributes: { level: 3 } },

これで、Ctrl + Alt + Hを押したときにH3見出しへ変換されます。


注意点

1. クラシックエディターでは使えない

このスニペットは、ブロックエディター用です。

クラシックエディターでは動作しません。


2. 一部のブロックでは完全変換できない場合がある

画像ブロック、カラムブロック、テーブルブロック、特殊なプラグインブロックなどは、文章系ブロックのように単純変換できない場合があります。

このスニペットでは、できるだけテキストを取り出して変換するようにしていますが、複雑なブロックでは完全に内容を保持できない可能性があります。


3. ブラウザやOSのショートカットと重なる可能性がある

Ctrl + Altの組み合わせは比較的衝突しにくいですが、環境によってはブラウザ、OS、入力ソフト側のショートカットと重なる場合があります。

反応しないキーがある場合は、ショートカットキーを別のキーに変更してください。


4. 保存前に動作確認する

本番サイトに追加する前に、テスト環境やステージング環境で確認するのが安全です。

特に、ブロックエディターに関連するカスタマイズは、WordPress本体やプラグインの影響を受けることがあります。


このスニペットが便利な場面

このスニペットは、記事を大量に書く人ほど効果があります。

たとえば、次のような場面で便利です。

作業内容便利になる理由
ブログ記事の見出し整理H2、H3への変更が速くなる
メモを記事に整形する段落、リスト、引用へ素早く変換できる
コード解説記事を書くコードブロックへの変更が速くなる
長文記事を編集するマウス操作を減らせる
複数ブロックをまとめるグループ化が楽になる

ブロック変換は小さな操作ですが、記事作成中に何十回も行う作業です。

ショートカット化することで、編集スピードを上げられます。


まとめ

今回は、WordPressのブロックエディターで、ショートカットキーからブロックの種類を変更するスニペットを紹介しました。

このスニペットを使うと、段落、見出し、リスト、引用、コード、整形済みテキスト、グループ化といった操作をキーボードだけで実行できます。

特に、ブログ記事や長文コンテンツをよく作成する場合、ブロック変換の手間を大きく減らせます。

Code Snippetsに追加すれば、テーマファイルを直接編集せずに導入できます。

WordPressで記事作成の効率を上げたい場合は、かなり実用的なカスタマイズです。