4

I'm working on a site that involves taking a URL and then getting information about that URL using an API. The URL is entered into a field in a Matrix block, and then another field in the same Matrix block is populated automatically on save. This is working, but only when the article is saved. I'd also like it to work in live preview mode. The event I'm using on save is entries.saveEntry, but I've also tried using entryRevisions.onSaveDraft, hoping that drafts were saved with live preview changes, but that didn't work.

Question: Is there some event I can listen for that lets me know when the live preview is about to refresh? Can this be done with JavaScript in the admin instead? If so, is there documentation that describes how to do this?

2

Attaching an event listener for the iframe's load event is a very good way to solve this. The issue is how/when to attach that listener, as the .lp-iframe isn't attached to the DOM before the user actually enters Live Preview.

Craft exposes the current Live Preview instance (via the global Craft JavaScript object that is available inside the CP) and the Live Preview instance dispatches an enter event when the user enters Live Preview – at which point the .lp-iframe should be available in the DOM. This means that you need to listen for the livePreview.enter event, before attempting to attach the load event listener for the iframe.

The only trouble is actually getting to the Craft.livePreview instance, as your plugin's JavaScript will usually run before it is created. This means that if you try to read the Craft.livePreview attribute in your plugin's JavaScript on pageload, you'll just get an undefined value back.

One way to solve this, is to poll for the Craft.livePreview instance. Polling basically involves testing for the attribute continually, until its available (or after a reasonable amount of time has passed, and we can assume the attribute will never be available – at which point its a good idea to stop polling to avoid memory leaks etc).

Here's a script which will poll the Craft object for the livePreview instance for a full 2 seconds (should be more than enough!) using requestAnimationFrame (which basically means it'll check as often as 60 times each second).

If/when the livePreview instance is found, the script attaches event listeners to the enter and exit events. Inside the former event's listener (which will fire when Live Preview opens), one more event listener is attached for the .lp-iframe element's load event, so that finally, when the iframe loads, the onLivePreviewIframeLoaded event handler is called. Inside that last event handler, you should hopefully be able to accomplish what you need.

The following should be added to the init method in your plugin's primary class:

// Only add the JS for non-AJAX CP requests
if (craft()->request->isCpRequest() && !craft()->request->isAjaxRequest()) {

    craft()->templates->includeJs("

        function getLivePreview()
        {

            // Remove the RAF poller if it exists
            if (livePreviewPollerRaf) {
                Garnish.cancelAnimationFrame(livePreviewPollerRaf);
                livePreviewPollerRaf = null;
            }

            if (Craft.livePreview) {

                // I can haz live preview. Attach event listeners
                livePreview = Craft.livePreview;
                livePreview.on('enter', onLivePreviewEnter);
                livePreview.on('exit', onLivePreviewExit);

                console.log('Live preview is available');

            } else if (new Date().getTime() - now < 2000) { // Only poll for 2 seconds

                livePreviewPollerRaf = Garnish.requestAnimationFrame(livePreviewPoller);

            }
        }

        function onLivePreviewEnter()
        {
            Garnish.requestAnimationFrame(function () {
                $('.lp-iframe').on('load', onLivePreviewIframeLoaded);
            });
        }

        function onLivePreviewExit()
        {
            // Remove the load handler, lets be clean
            $('.lp-iframe').off('load');
        }

        function onLivePreviewIframeLoaded(e)
        {
            console.log('live preview iframe loaded', e);
            // ** THIS IS WHERE YOU CAN DO STUFF **
        }

        // Start by polling for the live preview instance
        var livePreview = null;
        var livePreviewPollerRaf = null;
        var livePreviewPoller = getLivePreview;
        var now = new Date().getTime();

        livePreviewPoller();

    ");

}
  • Excellent! Thank you very much for the in-depth writeup! I should be able to give this a try tomorrow. I'm hoping I can find a better way than polling to check for the existence of the iframe, but I'll test this first and mark answered when it does the trick. Thanks again! – Gavin Anderegg Jun 9 '16 at 23:46
  • No worries, @GavinAnderegg – hope it's helpful. I concur that polling isn't ideal, might be there's a better way, but I haven't found it. Something similar to the code above is actually in my Reasons plugin – please let me know if you figure it out without the polling ;) – Mats Mikkel Rummelhoff Jun 9 '16 at 23:50
  • This works really well. Thanks again for the help. I'll let you know if I figure out some way other than the polling. Thanks! – Gavin Anderegg Jun 10 '16 at 14:39
  • Awesome! I guess you could drop the polling and attach the Craft.livePreview.enter event handler when the Live Preview button is clicked (as per @spAnser's answer). Not sure if I'd consider that a better solution per se, but its an option :) – Mats Mikkel Rummelhoff Jun 11 '16 at 6:02
2

You will probably have to use javascript. Something like this would work.

$('.lp-iframe').on('load', function(event) {
  console.log(event);
});

edit

Try this to bind the lp-iframe after it gets added to the page.

$('.livepreviewbtn').on('click', bindIframe);

function bindIframe() {
  $('.livepreviewbtn').off('click', bindIframe);
  $('.lp-iframe').on('load', function(event) {
    console.log(event);
  });
}
  • I tried injecting this with craft()->templates->includeJs(), but nothing additional gets logged. – Gavin Anderegg Jun 9 '16 at 20:17
  • Looks like the lp-iframe is no available on the page until it is called. – spAnser Jun 9 '16 at 21:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.