UI Tips and Tricks – Inline Visualforce Resize

A real frustration with inline Visualforce pages (added to standard page layouts) is the static nature of the height setting. From the layout we can set a specific height, but ideally we want the height set dynamically from the content height. Sounds like a simple enough requirement, however, the fact that the VF page section is implemented as an iFrame loaded from another domain makes cross-domain communication a non-trivial task. Note, for security reasons browsers enforce a same-origin policy, preventing script running across domain boundaries. Workarounds to this restriction include the HTML5 postMessage function on the client and proxy services on the server. So the question becomes, how can the iFrame content communicate across domains to tell the host page the correct height for the iFrame? The answer to this is somewhat contrived, but hopefully my basic approach below tells a clear enough story.

Here we go.
1. The inline VF page contains an iFrame, into which we load a helper script file from the base salesforce domain with a height parameter in the querystring.

document.getElementById(‘helpframe’).src=’https://emea.salesforce.com{!$Resource.iFrameHelper}?height=’+h+’&iframename=MYPAGENAME&cacheb=’+Math.random();

The random parameter is there to avoid caching issues. Crucially as this helper script is running on the same domain as the standard page layout, it can call a script in the page itself. Note the helper script is loaded from a static resource. To keep the solution generic the page name is passed as a parameter also, handily the title attribute in the host page is set to the page name, we’ll use this later to find the id for the iFrame.

2. In the helper script we extract the 2 parameters from the querystring and call a script function in the host page (via parent.parent – which traverses up the DOM to the parent page).

3. In order to add script to the host page we use the Sidebar injection technique (or hack) and introduce a simple Javascript function (via a narrow component) which takes the page name and height, finding the former in the DOM using Ext.query (Ext is already referenced in the page), and setting the element height to the latter.

Example solution components::

0. Pre-requisites:
User Profiles must have the “Show Custom Sidebar On All Pages” General User Permission ticked.

1. Add a HTML file static resource, named iFrameHelper – content below.

<html>
  <body onload="parentIframeResize()">  
    <script>  
      // Tell the parent iframe what height the iframe needs to be 
      function parentIframeResize(){ 
         var height = getParam('height'); 
         var iframename = getParam('iframename');
         // This works as our parent's parent is on our domain.. 
         parent.parent.resizeIframe(height,iframename); 
      }  
      // Helper function, parse param from request string 
      function getParam(name){ 
        name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]"); 
        var regexS = "[\\?&]"+name+"=([^&#]*)"; 
        var regex = new RegExp( regexS ); 
        var results = regex.exec( window.location.href ); 
        if( results == null ) 
          return ""; 
        else 
          return results[1]; 
      }
    </script>  
  </body>  
</html>

2. Add a HTML sidebar component (narrow left) – click “Show HTML” and paste in markup below.

<script>
  function resizeIframe(h, ifn){
   var e = Ext.query("iframe[title='"+ifn+"']");  
   console.log(e); 
   var itarget = e[0].getAttribute('id'); 
   Ext.get(itarget).set({height: parseInt(h)+10});
  }
</script>

3. Add a Visualforce page named MyTestInlineVFPage – paste in markup below.

<apex:page docType="html-5.0" standardController="Account">
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  
  <script type="text/javascript">      
        function resizeParentIFrame(){
            var h = document.body.scrollHeight;
            //TODO - replace with the relevant page name.
            var iframename = 'MyTestInlineVFPage';
            //TODO - replace with the relevant base url - page runs on the VF domain so functions return the VF domain. 
            var baseUrlForInstance = 'https://emea.salesforce.com';
            document.getElementById('helpframe').src = baseUrlForInstance+'{!$Resource.iFrameHelper}?height='+h+'&iframename='+iframename+'&cacheb='+Math.random();
        }
        
        function forceParentIFrameResize(){
            document.getElementById('helpframe').src=document.getElementById('helpframe').src;
        }
        
        window.onload=function(){
            resizeParentIFrame();
        }
  </script>  
</apex:page>

4. Add the VF page to a new section on an Account layout.

The solution above needs further work in the areas below. I’m planning to improve this as part of a commercial AppExchange package I’m working on and will post the improved resize solution.

Code quality.
Exception handling.
Calculation of the base salesforce domain – currently hardcoded in the inline page.

I’d be delighted to hear about other improvements, or indeed alternative approaches.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: