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();
");
}