jquery - iframe access denied in IE on some pages

前端 未结 5 1443
深忆病人
深忆病人 2020-12-05 15:36

I\'m the author of printThis, a jquery plugin for printing.

https://github.com/jasonday/printThis

I have a user that has brought up an issue, that I have bee

相关标签:
5条回答
  • 2020-12-05 16:05

    This answer has already been stated in the original question UPDATE, but I wanted add a more succinct answer to the original question related to getting around the SCRIPT70 Permission denied error (I ran into this on IE11/Win7 with JQuery 3.2.1).

    Instead of $('<iframe .../>').appendTo($('body'))

    Do this:

    var $iframe = $('<iframe .../>');
    document.body.appendChild($iframe[0]);
    

    Answer taken from here: https://bugs.jquery.com/ticket/13936#comment:28

    0 讨论(0)
  • 2020-12-05 16:10

    Issue is due to IE not inheriting the parent document.domain.

    Unfortunately, once you get into this murky area, it took some specific hacks to get this to work properly.

    Basically doing a check for if document.domain is being explicitly set and the browser is IE.

    Full updated plugin:

    https://github.com/jasonday/printThis

    (function ($) {
        var opt;
        $.fn.printThis = function (options) {
            opt = $.extend({}, $.fn.printThis.defaults, options);
            var $element = this instanceof jQuery ? this : $(this);
    
                var strFrameName = "printThis-" + (new Date()).getTime();
    
                if(window.location.hostname !== document.domain && navigator.userAgent.match(/msie/i)){
                    // Ugly IE hacks due to IE not inheriting document.domain from parent
                    // checks if document.domain is set by comparing the host name against document.domain
                    var iframeSrc = "javascript:document.write(\"<head><script>document.domain=\\\"" + document.domain + "\\\";</script></head><body></body>\")";
                    var printI= document.createElement('iframe');
                    printI.name = "printIframe";
                    printI.id = strFrameName;
                    printI.className = "MSIE";
                    document.body.appendChild(printI);
                    printI.src = iframeSrc;
    
                } else {
                     // other browsers inherit document.domain, and IE works if document.domain is not explicitly set
                    var $frame = $("<iframe id='" + strFrameName +"' name='printIframe' />");
                    $frame.appendTo("body");
                }
    
    
                var $iframe = $("#" + strFrameName);
    
                // show frame if in debug mode
                if (!opt.debug) $iframe.css({
                    position: "absolute",
                    width: "0px",
                    height: "0px",
                    left: "-600px",
                    top: "-600px"
                });
    
    
            // $iframe.ready() and $iframe.load were inconsistent between browsers    
            setTimeout ( function () {
    
                var $doc = $iframe.contents();
    
                // import page stylesheets
                if (opt.importCSS) $("link[rel=stylesheet]").each(function () {
                    var href = $(this).attr("href");
                    if (href) {
                        var media = $(this).attr("media") || "all";
                        $doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + href + "' media='" + media + "'>")
                    }
                });
    
                //add title to iframe
                if (opt.pageTitle) $doc.find("head").append("<title>" + opt.pageTitle + "</title>");
    
                // import additional stylesheet
                if (opt.loadCSS) $doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + opt.loadCSS + "'>");
    
                // grab $.selector as container
                if (opt.printContainer) $doc.find("body").append($element.outer());
    
                // otherwise just print interior elements of container
                else $element.each(function () {
                    $doc.find("body").append($(this).html())
                });
    
                if($iframe.hasClass("MSIE")){
                    // check if the iframe was created with the ugly hack
                    // and perform another ugly hack out of neccessity
                    window.frames["printIframe"].focus();
                    setTimeout(function () {
                       $doc.find("head").append("<script>  window.print(); </script>");
                    }, 500 );
                } else {
                    // proper method
                    $iframe[0].contentWindow.focus();
                    $iframe[0].contentWindow.print();  
                }
    
                 //remove iframe after print
                if (!opt.debug) {
                    setTimeout(function () {
                        $iframe.remove();
                    }, 1000);
                }
    
    
            }, 333 );
    
        };
    
        // defaults
        $.fn.printThis.defaults = {
            debug: false,           // show the iframe for debugging
            importCSS: true,        // import parent page css
            printContainer: true,   // print outer container/$.selector
            loadCSS: "",            // load an additional css file
            pageTitle: ""           // add title to print page
        };
    
        // $.selector container
        jQuery.fn.outer = function () {
            return $($("<div></div>").html(this.clone())).html()
        }
    })(jQuery);
    
    0 讨论(0)
  • 2020-12-05 16:11

    In your code, you are using setTimeout to execute your function after the iframe has loaded.

    // allow iframe to fully render before action
    setTimeout ( function () {
    ...
    }, 333 );  //333ms
    

    but this is a mistake as you don't know if the time given is enough to load the iframe or not. Javascript execution is asynchronous so, there is no guarantee that setTimeout will offset the execution of the function until iframe loads. Since load time is different for different pages. Some cannot execute the code properly, pointing to the line which you find to be causing errors.

    var $doc = $("#" + strFrameName).contents();  //only after loading
    

    The correct way is to use event load or onload to get to know if the DOM object has loaded properly or not.

    <script>
    document.getElementById("myframe").onload = function() {
      alert("myframe is loaded");
    };
    </script>
    //or
    <iframe id="myFrame" onload="myFunction();"></iframe>
    
    0 讨论(0)
  • 2020-12-05 16:20

    IE works with iframe like all the other browsers (at least for main functions). You just have to keep a set of rules:

    • before you load any javascript in the iframe (that part of js which needs to know about the iframe parent), ensure that the parent has document.domain changed.
    • when all iframe resources are loaded, change document.domain to be the same as the one defined in parent. (You need to do this later because setting domain will cause the iframe resource's request to fail)

    • now you can make a reference for parent window: var winn = window.parent

    • now you can make a reference to parent HTML, in order to manipulate it: var parentContent = $('html', winn.document)
    • at this point you should have access to IE parent window/document and you can change it as you wont
    0 讨论(0)
  • 2020-12-05 16:22

    As long as you set iframe src, the same origin has to be verified against parent element, even if you set it as 'about:blank'. I guess IE fails in proper checkng, or some javascript ran and set the document.location to different one than the iframe is created.

    How about NOT setting src at all like the following? it still should work.

    var $iframe = $("<iframe id='" + strFrameName +"'/>");
    $iframe.appendTo("body");
    var $iframeDoc = $iframe[0].contentWindow.document;
    
    $iframeDoc.open();
    $iframeDoc.write("foo");
    $iframeDoc.close();
    
    0 讨论(0)
提交回复
热议问题