How to set custom http headers when changing iframe src?

Solution 1:

You can have the results of an ajax request that has custom headers be set as the content of an iframe like so:

$.ajax({
    type: "GET", 
    url: "https://app.icontact.com/icp/a/",
    contentType: "application/json",
    beforeSend: function(xhr, settings){
            xhr.setRequestHeader("some_custom_header", "foo");},
    success: function(data){
        $("#output_iframe_id").attr('src',"data:text/html;charset=utf-8," + escape(data))
    }
});

This is assuming the iframe is pointing at a cross domain src. It is simpler if everything is on the same domain.

Edit: Maybe try this variation.

$.ajax({
    type: "GET", 
    url: "https://app.icontact.com/icp/a/",
    contentType: "application/json",
    beforeSend: function(xhr, settings){
            xhr.setRequestHeader("some_custom_header", "foo");},
    success: function(data){
        $("#output_iframe_id").attr('src',"/")
        $("#output_iframe_id").contents().find('html').html(data); 
    }
});

Solution 2:

Rather than using a data URI, or setting the contents to a string, you can use URL.createObjectURL(), and set it as the src of the iframe.

var xhr = new XMLHttpRequest();

xhr.open('GET', 'some.pdf');
xhr.onreadystatechange = handler;
xhr.responseType = 'blob';
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
xhr.send();

function handler() {
  if (this.readyState === this.DONE) {
    if (this.status === 200) {
      // this.response is a Blob, because we set responseType above
      var data_url = URL.createObjectURL(this.response);
      document.querySelector('#output-frame-id').src = data_url;
    } else {
      console.error('no pdf :(');
    }
  }
}

The object URLs are pretty interesting. They're of the form blob:https://your.domain/1e8def13-3817-4eab-ad8a-160923995170. You can actually open them in a new tab and see the response, and they're discarded when the context that created them is closed.

Here's a full example: https://github.com/courajs/pdf-poc

Solution 3:

I ended up going with the approach proposed by the other answers here, that use ajax to get the html string and then directly set the contents of the iFrame.

However, I used the approach posted in this answer to actually set the contents of the iFrame, as I found it worked well cross platform with almost all devices I could dig up.

Tested - successful:

  • Chrome 54 (desktop) ^
  • Firefox 49 (desktop) ^
  • IE 11 (desktop) ^
  • IE 10 (desktop) in emulation mode ^
  • Safari/Chrome on iOS 8 (ipad)
  • Chrome on Android 6 (nexus phone)
  • Edge on Lumia 950 (Win 10 Phone)

^ confirmed that linked css and js in the content run correctly (others not tested)

Tested - unsuccessful:

  • IE 9 (desktop) in emulation mode
  • Safari/Chrome on iOS 7 (iPhone)

So putting them together gives something like this (Note: I havn't actually run this exact code):

$.ajax({
    type: "GET", 
    url: "https://yourdomain.com/gethtml",
    beforeSend: function(xhr) {
        xhr.setRequestHeader("yourheader", "value");
    },
    success: function(data) {
        var iframeDoc = document.querySelector('#myiframe').contentWindow.document;
        iframeDoc.open('text/html', 'replace');
        iframeDoc.write(data);
        iframeDoc.close();
    }
});

Here's an example of setting the iFrame contents in this JS Bin

Edit: Here's the html part

<iframe id="myiframe" src="about:blank"></iframe>

Edit 2:

The solution above appears to no longer be working in Firefox (50.1.0) for some unknown reason. Using the solution in this answer I've now changed to code to the example below, which also seems to be more robust:

$.ajax({
    type: "GET", 
    url: "https://yourdomain.com/gethtml",
    beforeSend: function(xhr) {
        xhr.setRequestHeader("yourheader", "value");
    },
    success: function(data) {
        var iframe = document.getElementById('myiframe');
        iframe.contentWindow.contents = data;
        iframe.src = 'javascript:window["contents"]';
    }
});

Solution 4:

The following code works. It is a modification of the code provided by Matthew Graves, modified to use the srcdoc attribute to solve the problem of CSS and JavaScript references not been ran. Unfortunately, it is only working in Chrome.

 $.ajax({
        type: "GET", 
        url: "https://app.icontact.com/icp/a/",
        contentType: "application/json",
        beforeSend: function(xhr, settings){
                xhr.setRequestHeader("some_custom_header", "foo");},
        success: function(data){
            $("#output_iframe_id").attr('srcdoc',data)
        }
    });

Edit: Finally, I have resolved the issue of the scripts blocks cross-browser, by reassigning them to the iframe on document.ready function:

$(document).ready(function () {
    var doc = $(document);
    if (frames.length > 0) {
        doc = frames[0].document;
        $(doc).find('script').each(function () {
            var script = document.createElement("script");
            if ($(this).attr("type") != null) script.type = $(this).attr("type");
            if ($(this).attr("src") != null) script.src = $(this).attr("src");
            script.text = $(this).html();
            $(doc).find('head')[0].appendChild(script);
            $(this).remove();
        });
    }
});