How to load an additional script for a block in the block editor?

BUT, for some reason, the accordion function from jQuery UI doesn’t get attached to the .accordion class of my block in the block editor, so the accordion effect doesn’t work.

The ServerSideRender component uses the REST API to fetch the dynamic block output, hence the above issue happens because by the time the DOM is ready (which is when jQuery.ready()‘s callbacks run), the REST API AJAX call is not yet resolved or may have not even started, therefore the accordion div isn’t yet attached to the DOM.

Should I load the accordion-jquery-start.js script differently?

Yes, kind of — ServerSideRender doesn’t (yet) provide a “on-content-loaded” or “on-AJAX-complete/resolved” event that we could listen to, so you’d need to manually check if your div is already in the DOM and if so, run the $( '.accordion' ).accordion().

And in the modern world of JS, an easy way to do that is by using the MutationObserver API.

Please check the MDN web docs (see link above) for more details about what is the mutation observer API, but in specific to Gutenberg or the block editor, and a dynamic block, here’s a working example you can try and learn from. 🙂

So in my index.js file, I got these: ( the console.log() are just for debugging purposes, so don’t forget to remove them later )

  1. Load WordPress dependencies:

    import { registerBlockType } from '@wordpress/blocks';
    import { useRef, useLayoutEffect } from '@wordpress/element';
    import { useBlockProps } from '@wordpress/block-editor';
    import ServerSideRender from '@wordpress/server-side-render';
    
  2. The MutationObserver callback:

    // This function checks if the accordion div has been attached to the DOM, and
    // if so, initializes jQuery accordion on that very div. So this function can be
    // placed at the top in your file (before you call registerBlockType()).
    function initAccordion( mutations ) {
        for ( let mutation of mutations ) {
            if ( 'childList' === mutation.type && mutation.addedNodes[0] &&
                // Good, the added node has an accordion div.
                jQuery( '.accordion', mutation.addedNodes[0] ).length >= 1
            ) {
                // Convert the div to a jQuery accordion.
                jQuery( mutation.addedNodes[0] ).accordion( {
                    header: 'h3',
                    heightStyle: 'content',
                    animate: 100,
                    collapsible: true,
                    active: false
                } );
                console.log( 'accordion initialized' );
            }
        }
    }
    
  3. Now here’s my block type:

    registerBlockType( 'my/accordion-block', {
        apiVersion: 2,
        title: 'My Accordion Block',
        category: 'widgets',
        edit() {
            // Create a ref for the block container.
            const ref = useRef( null );
    
            // Initialize the mutation observer once the block is rendered.
            useLayoutEffect( () => {
                let observer;
    
                if ( ref.current ) {
                    observer = new MutationObserver( initAccordion );
    
                    // Observe DOM changes in our block container only.
                    observer.observe( ref.current, {
                        childList: true,
                        subtree: true,
                    } );
                }
    
                // Cleanup function which stops the mutation observer.
                return () => {
                    if ( observer ) {
                        observer.disconnect();
                        console.log( 'observer disconnected' );
                    }
                };
            }, [] );
    
            // Pass the ref to the block container.
            // Note: { ref } is short for { ref: ref }
            const blockProps = useBlockProps( { ref } );
    
            return (
                <div { ...blockProps }>
                    <ServerSideRender block="my/accordion-block" />
                </div>
            );
        }
    } );