Answers.Body: <p>Attaching an event listener for the iframe's <code>load</code> event is a very good way to solve this. The issue is how/when to attach that listener, as the <code>.lp-iframe</code> isn't attached to the DOM before the user actually enters Live Preview.</p>
  
  <p>Craft exposes the current Live Preview instance (via the global <code>Craft</code> JavaScript object that is available inside the CP) and the Live Preview instance dispatches an <code>enter</code> event when the user enters Live Preview – at which point the <code>.lp-iframe</code> should be available in the DOM. This means that you need to listen for the <code>livePreview.enter</code> event, before attempting to attach the <code>load</code> event listener for the iframe.</p>
  
  <p>The only trouble is actually getting to the <code>Craft.livePreview</code> instance, as your plugin's JavaScript will usually run before it is created. This means that if you try to read the <code>Craft.livePreview</code> attribute in your plugin's JavaScript on pageload, you'll just get an <code>undefined</code> value back.</p>
  
  <p>One way to solve this, is to <em>poll</em> for the <code>Craft.livePreview</code> 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 <em>never</em> be available – at which point its a good idea to stop polling to avoid memory leaks etc).</p>
  
  <p>Here's a script which will poll the <code>Craft</code> object for the <code>livePreview</code> instance for a full 2 seconds (should be more than enough!) using <code>requestAnimationFrame</code> (which basically means it'll check as often as 60 times each second). </p>
  
  <p>If/when the <code>livePreview</code> instance is found, the script attaches event listeners to the <code>enter</code> and <code>exit</code> events. Inside the former event's listener (which will fire when Live Preview opens), one more event listener is attached for the <code>.lp-iframe</code> element's <code>load</code> event, so that finally, when the iframe loads, the <code>onLivePreviewIframeLoaded</code> event handler is called. Inside that last event handler, you should hopefully be able to accomplish what you need.</p>
  
  <p>The following should be added to the <code>init</code> method in your plugin's primary class:</p>
  
  <pre><code>// Only add the JS for non-AJAX CP requests
  if (craft()-&gt;request-&gt;isCpRequest() &amp;&amp; !craft()-&gt;request-&gt;isAjaxRequest()) {
  
      craft()-&gt;templates-&gt;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 &lt; 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();
  
      ");
  
  }
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-06-09T23:46:43.027Z
Answers.Text: 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!
Answers.UserDisplayName: Gavin Anderegg
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-06-09T23:50:08.813Z
Answers.Text: 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](https://github.com/mmikkel/Reasons-Craft) plugin – please let me know if you figure it out without the polling ;)
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-06-10T14:39:03.807Z
Answers.Text: 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!
Answers.UserDisplayName: Gavin Anderegg
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-06-11T06:02:04.833Z
Answers.Text: 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 :)
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-06-09T23:09:24.263Z
Answers.LastActivityDate: 2016-06-09T23:16:06.057Z
Answers.LastEditDate: 2016-06-09T23:16:06.057Z
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Answers.Body: <p>You will probably have to use javascript. Something like this would work.</p>
  
  <pre><code>$('.lp-iframe').on('load', function(event) {
    console.log(event);
  });
  </code></pre>
  
  <p><em>edit</em></p>
  
  <p>Try this to bind the lp-iframe after it gets added to the page. </p>
  
  <pre><code>$('.livepreviewbtn').on('click', bindIframe);
  
  function bindIframe() {
    $('.livepreviewbtn').off('click', bindIframe);
    $('.lp-iframe').on('load', function(event) {
      console.log(event);
    });
  }
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-06-09T20:17:29.390Z
Answers.Text: I tried injecting this with craft()->templates->includeJs(), but nothing additional gets logged.
Answers.UserDisplayName: Gavin Anderegg
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-06-09T21:51:30.353Z
Answers.Text: Looks like the lp-iframe is no available on the page until it is called.
Answers.UserDisplayName: spAnser
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-06-09T20:00:03.370Z
Answers.LastActivityDate: 2016-06-09T22:06:03.450Z
Answers.LastEditDate: 2016-06-09T22:06:03.450Z
Answers.UserDisplayName: spAnser
Body: <p>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 <code>entries.saveEntry</code>, but I've also tried using <code>entryRevisions.onSaveDraft</code>, hoping that drafts were saved with live preview changes, but that didn't work.</p>
  
  <p><strong>Question:</strong>
  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?</p>
  
Tags: <plugin-development><events><live-preview>
Title: Is there a way a plugin can watch for live preview updates?
ThreadText: # Is there a way a plugin can watch for live preview updates?
  
  (Asked by: username_0 on 2016-06-09)
  
  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?
  
  **Tags:** plugin-development, events, live-preview
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_1 on 2016-06-09. Score: 2 | Accepted answer
  
  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();
  
      ");
  
  }
  
  ```
  
  > **Comments:**
  > **username_0**: 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!  
  > **username_1**: No worries, @username_0 – 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](https://github.com/mmikkel/Reasons-Craft) plugin – please let me know if you figure it out without the polling ;)  
  > **username_0**: 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!  
  > **username_1**: 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 @username_2's answer). Not sure if I'd consider that a better solution per se, but its an option :)  
  
  ---
  
  ### Answer by username_2 on 2016-06-09. Score: 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);
    });
  }
  
  ```
  
  > **Comments:**
  > **username_0**: I tried injecting this with craft()->templates->includeJs(), but nothing additional gets logged.  
  > **username_2**: Looks like the lp-iframe is no available on the page until it is called.  
  
  ---
  
  