Blocks in an iframed (template) editor

The new template editor is loaded in an iframeiframe iFrame is an acronym for an inline frame. An iFrame is used inside a webpage to load another HTML document and render it. This HTML document may also contain JavaScript and/or CSS which is loaded at the time when iframe tag is parsed by the user’s browser. to isolate it from the rest of the adminadmin (and super admin) screen. This has the following benefits:

  • Admin styles no longer affect the editor content, so there’s no need to reset any of these rules.
  • Content styles no longer affect the admin screen, so blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. and theme CSSCSS Cascading Style Sheets. rules no longer need to be prefixed.
  • Viewport relative CSS units will work correctly. The dimensions of the editor content is usually not the same as the dimensions of the admin page, so without an iframe units like vw will be relative to the admin page.
  • Media queries will also work natively, without needing to fake them, as we did before, which is fragile.
  • In general, it makes the lives of block and theme authors easier because styles from the front-end can be dropped in with very little, if nothing, to adjust. This also applies to lighter blocks, where the editor DOM structure matches the front-end, which we highly recommend when possible.
  • With a separate window for the editor content, it’s possible for the selection in the editor to remain visible while also having a (collapsed) selection in the editor UIUI User interface, for example an input field for a URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org.

We currently only iframe new editors. While the new template editor has been iframed, the post editor remains unchanged. We do this to gradually test how existing blocks from plugins work within an iframed editor, since there are cases where a block could look broken or (less likely) error. We hereby urge pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party authors to test their blocks with the new template editor and contact us if they need help to adjust blocks to work in the iframe.

Document and window

The iframe will have a different document and window than the admin page, which is now the parent window. Editor scripts are loaded in the admin page, so accessing the document or window to do something with the content will no longer work.

Most blocks written in ReactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org/. should continue to work properly, except if you rely on document or window. To fix, you need to create ref to access the relative document (ownerDocument) or window (defaultView). Regardless of the iframe, it is good practice to do this and avoid the use of globals.

const ref = useRef();
useEffect( () => {
  const { ownerDocument } = ref.current;
  const { defaultView } = ownerDocument;
  // Set ownerDocument.title for example.
}, [] );
const props = useBlockProps( { ref } );

If you attach event handlers, remember that the useEffect callback will not be called if the ref changes, so it is good practice to use the new useRefEffect APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways., which will call the given callback if the ref change in addition to any dependencies passed.

const ref = useRefEffect( ( element ) => {
  const { ownerDocument } = element;
  const { defaultView } = ownerDocument;
  defaultView.addEventListener( ... );
  return () => {
	defaultView.removeEventListener( ... );
  };
}, [] );
const props = useBlockProps( { ref } );

Other frameworks and libraries

For the editor, scripts such as jQuery are loaded in the parent window (admin page), which is fine. When using these to interact with a block in the iframe, you should pass the element reference.

const ref = useRefEffect( ( element ) => {
  jQuery( element ).masonry( … );
  return () => {
    defaultView.jQuery( element ).masonry( 'destroy' );
  }
}, [] );
const props = useBlockProps( { ref } )

But what if the library is using the global window or document and it’s out of your control?

Submit an issue or PR for the library to use ownerDocument and defaultView instead of the globals. Ideally, any library should allow initialisation with an element in an iframe as the target. It’s never impossible. Feel free to contact us to mention the issue.

In the meantime, you can use the script that is loaded inside the iframe. We’ve loaded all front-end scripts in the iframe to fix these cases, but note that ideally you shouldn’t use scripts loaded in the iframe at all. You can use defaultView to access the script.

const ref = useRefEffect( ( element ) => {
  const { ownerDocument } = element;
  const { defaultView } = ownerDocument;

  // Use the script loaded in the iframe.
  // Script are loaded asynchronously, so check is the script is loaded.
  // After the dependencies have loaded, the block will re-render.
  if ( ! defaultView.jQuery ) {
    return;
  }

  defaultView.jQuery( element ).masonry( … );
  return () => {
    defaultView.jQuery( element ).masonry( 'destroy' );
  }
} );
const props = useBlockProps( { ref } );

And that’s it! In summary, any problem that a block might have in the iframe is caused by using the document or window global at the root of it, either in the block’s code or a third party library. Ideally, all code uses the ownerDocument and defaultView relative properties.

#5-8, #dev-notes, #gutenberg