Skip to content

Instantly share code, notes, and snippets.

@KevinBatdorf
Last active July 17, 2024 14:36
Show Gist options
  • Save KevinBatdorf/fca19e1f3b749b5c57db8158f4850eff to your computer and use it in GitHub Desktop.
Save KevinBatdorf/fca19e1f3b749b5c57db8158f4850eff to your computer and use it in GitHub Desktop.
WordPress check Gutenberg editor is ready
import { select, subscribe } from '@wordpress/data'
export function whenEditorIsReady() {
return new Promise((resolve) => {
const unsubscribe = subscribe(() => {
// This will trigger after the initial render blocking, before the window load event
// This seems currently more reliable than using __unstableIsEditorReady
if (select('core/editor').isCleanNewPost() || select('core/block-editor').getBlockCount() > 0) {
unsubscribe()
resolve()
}
})
})
}
@strarsis
Copy link

strarsis commented Nov 9, 2022

@KevinBatdorf: Awesome, needed this! Could be put into a npm package.

@strarsis
Copy link

strarsis commented Nov 9, 2022

Here a function that uses whenEditorIsReady and also waits for the editor iframe becoming ready:

import whenEditorIsReady from './when-editor-is-ready.js';

async function getEditorIframe() {
  await whenEditorIsReady();

  const editorCanvasIframeElement = document.querySelector('[name="editor-canvas"]');

  return new Promise((resolve) => {
    if(!editorCanvasIframeElement.loading) {
      // somehow the iframe has already loaded,
      // skip waiting for onload event (won't be triggered)
      resolve(editorCanvasIframeElement);
    }

    editorCanvasIframeElement.onload = () => {
      resolve(editorCanvasIframeElement);
    };
  });
}

export default getEditorIframe;

@X-Raym
Copy link

X-Raym commented Feb 13, 2023

Hi, How would you import that into a regular plugin javascript?
Import and Export doesnt work with enqueued scripts
thx!

@strarsis
Copy link

strarsis commented Feb 13, 2023

@X-Raym: For those themes and plugins I would build the scripts, so those imports is compiled.

Alternatively you can also enregister the when-editor-is-ready.js and then enqueue the theme script, with the previously enregistered when-editor-is-ready as a dependency. This will make WordPress first enqueue the when-editor-is-ready script and then the theme script. You would not use import in the theme script then, but rather check for the whenEditorIsReady function being available (which it should be, when everything was correctly enregistered and enqueued).

You may still want to minify those script files (and enqueue the .min.js variant, notably on production).

@X-Raym
Copy link

X-Raym commented Feb 13, 2023

@strarsis Hi,
No compilation would be the best, so I can encapsulate the function in a more traditional JS with toher functions,
But you speak about theme script, but this is for Gutenberg Is Ready event, so it is on admin side, no theme script.
How would you enqueue this then?

Just putting

async function getEditorIframe() {
    await whenEditorIsReady();

    const editorCanvasIframeElement = document.querySelector('[name="editor-canvas"]');

    return new Promise((resolve) => {
      if(!editorCanvasIframeElement.loading) {
        // somehow the iframe has already loaded,
        // skip waiting for onload event (won't be triggered)
        resolve(editorCanvasIframeElement);
        console.log('ready')
      }

      editorCanvasIframeElement.onload = () => {
        resolve(editorCanvasIframeElement);
      };
    });
  }
function test_function( $hook ) {
    wp_enqueue_script( 'my_custom_script', plugin_dir_url( __FILE__ ) . 'js/my-new-script.js', 100, null, true );
}
add_action( 'admin_enqueue_scripts', 'test_function' );

Doesnt do anything (note that the JS link is good of course, the php function is runnning etc. It is just the Javascript itself which doesnt work. Not exactly sure also where to plug my functions for when the editor is ready)

@strarsis
Copy link

strarsis commented Feb 13, 2023

Instead of my_custom_script you should name that helper script file something like gutenberg-when-editor-is-ready.
Then you would add this script handle to the dependencies array for the admin script that uses whenEditorIsReady:

function test_function( $hook ) {
    wp_enqueue_script( 'gutenberg-when-editor-is-ready', plugin_dir_url( __FILE__ ) . 'js/my-new-script.js', [], null );

   // (or at another place)
    wp_enqueue_script( 'my-admin-script', plugin_dir_url( __FILE__ ) . 'js/my-admin-script.js', ['gutenberg-when-editor-is-ready'], null, true );
}
add_action( 'admin_enqueue_scripts', 'test_function' );

Additionally, the function signature of wp_enqueue_script is,
wp_enqueue_script( string $handle, string $src = '', string[] $deps = array(), string|bool|null $ver = false, bool $in_footer = false ),
so in your code example you probably passed some incorrect arguments to it.

@X-Raym
Copy link

X-Raym commented Feb 13, 2023

so in your code example you probably passed some incorrect arguments to it.

I just quickly renamed things to have more clarity, but on my side it works (the script is loaded etc).

Your code uses two javascripts files, I was expecting to merge the event handler and my custom scripts into one js only, as I dont need the functions to be avalaible for other scripts. That's why I needed a version without import/export. This way to enqueue triggers the import/export error as well, and if I remove these statement, I still dont succeed to trigger my custom functions at editor ready.

I'm a bit confused by the javascript side of thing, I think I would need a full working exemple with console.log call at editor ready, something like that.

Thx for the help and explications anyway!

@KevinBatdorf
Copy link
Author

@X-Raym instead of importing it just copy/paste the function at the start of the file.

@strarsis Thanks for that iframe code. I actually need to convert some cypress to use the upcoming iframe wrapper and I think I'll need just that.

@strarsis
Copy link

strarsis commented Feb 13, 2023

@X-Raym: Well, you can put that code just into a javascript file, together with the code that uses the function, and then enqueue it for editor.

const select = wp.data.select,
   subscribe = wp.data.subscribe;

function whenEditorIsReady() {
    return new Promise((resolve) => {
        const unsubscribe = subscribe(() => {
            // This will trigger after the initial render blocking, before the window load event
            // This seems currently more reliable than using __unstableIsEditorReady
            if (select('core/editor').isCleanNewPost() || select('core/block-editor').getBlockCount() > 0) {
                unsubscribe()
                resolve()
            }
        })
    })
}

async function getEditorIframe() {
  await whenEditorIsReady();

  const editorCanvasIframeElement = document.querySelector('[name="editor-canvas"]');

  return new Promise((resolve) => {
    if(!editorCanvasIframeElement.loading) {
      // somehow the iframe has already loaded,
      // skip waiting for onload event (won't be triggered)
      resolve(editorCanvasIframeElement);
    }

    editorCanvasIframeElement.onload = () => {
      resolve(editorCanvasIframeElement);
    };
  });
}


// Wait for editor iframe becoming available (and editor becoming ready)
await getEditorIframe();
// your code here, when editor became available

@X-Raym
Copy link

X-Raym commented Feb 14, 2023

@strarsis thx you very much for this more detailed code snippet!

It do return an error though:

```Uncaught SyntaxError: await is only valid in async functions, async generators and modules````

@X-Raym
Copy link

X-Raym commented Feb 14, 2023

Ok I had to replace the last async call with

(async() => {
    await getEditorIframe();
    MyFunction()
  })();

And also

document.querySelector('[name="editor-canvas"]');

by

document.querySelector('.edit-post-header-toolbar__left');

(cause the first one was undefined. I needed the toolbar anyway), cause I use this to print custom infos on the Gutenberg toolbar.

Seems to works with these fixes, many thx!

img

@strarsis
Copy link

🤔 In latest Gutenberg it appears that no iframe element is used anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment