jquery – IE浏览器在某些页面上拒绝iframe访问

我是printThis的作者,这是一个用于打印的jquery插件。

https://github.com/jasonday/printThis

我有一个用户提出了一个问题,我一直无法破解,不幸的是,我无法分享页面(隐私问题)。

在用户的网站上,问题出现在IE中的某些页面上,而不是其他页面上。 打印失败,因为iframe仍然是空的。

IE中的错误在jQuery中:

contents: function (a) { return f.nodeName(a, "iframe") ? a.contentDocument || a.contentWindow.document : f.makeArray(a.childNodes) } 

使用日志记录,我能够确定它在这条线路上失败了:

 var $doc = $("#" + strFrameName).contents(); 

但同样,这只发生在某些页面上,而且我无法在该用户网站之外的任何实例中重新创建。

我的问题:这里有更好的方法吗? 或者使$doc对象更具防弹性的方法?


 // ----------------------------------------------------------------------- // printThis v1.1 // Printing plug-in for jQuery // // Resources (based on) : // jPrintArea: http://plugins.jquery.com/project/jPrintArea // jqPrint: https://github.com/permanenttourist/jquery.jqprint // Ben Nadal: http://www.bennadel.com/blog/1591-Ask-Ben-Print-Part-Of-A-Web-Page-With-jQuery.htm // // Dual licensed under the MIT and GPL licenses: // http://www.opensource.org/licenses/mit-license.php // http://www.gnu.org/licenses/gpl.html // // (c) Jason Day 2012 // // Usage: // // $("#mySelector").printThis({ // debug: false, //show the iframe for debugging // importCSS: true, // import page CSS // printContainer: true, // grab outer container as well as the contents of the selector // loadCSS: "path/to/my.css" //path to additional css file // }); // // Notes: // - the loadCSS option does not need @media print //------------------------------------------------------------------------ (function($) { var opt; $.fn.printThis = function (options) { opt = $.extend({}, $.fn.printThis.defaults, options); var $element = (this instanceof jQuery) ? this : $(this); // if Opera, open a new tab if ($.browser.opera) { var tab = window.open("","Print Preview"); tab.document.open(); } // add dynamic iframe to DOM else { var strFrameName = ("printThis-" + (new Date()).getTime()); var $iframe = $(""); if (!opt.debug) { $iframe.css({ position: "absolute", width: "0px", height: "0px", left: "-600px", top: "-600px" }); } $iframe.appendTo("body"); } // allow iframe to fully render before action setTimeout ( function () { if ($.browser.opera) { var $doc = tab.document; } else { var $doc = $("#" + strFrameName).contents(); } // import page css 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(""); } }); } // add another stylesheet if (opt.loadCSS) { $doc.find("head").append(""); } //add title of the page if (opt.titlePage) { $doc.find("head").append(''+opt.titlePage+''); } //grab outer container if (opt.printContainer) { $doc.find("body").append($element.outer()); } else { $element.each( function() { $doc.find("body").append($(this).html()); }); } //$doc.close(); // print ($.browser.opera ? tab : $iframe[0].contentWindow).focus(); setTimeout( function() { ($.browser.opera ? tab : $iframe[0].contentWindow).print(); if (tab) { tab.close(); } }, 1000); //removed iframe after 60 seconds setTimeout( function(){ $iframe.remove(); }, (60 * 1000) ); }, 333 ); } $.fn.printThis.defaults = { debug: false, //show the iframe for debugging importCSS: true, // import page CSS printContainer: true, // grab outer container as well as the contents of the selector loadCSS: "", //path to additional css file titlePage: "" //add title to print page }; jQuery.fn.outer = function() { return $($('
').html(this.clone())).html(); } })(jQuery);

UPDATE

由于document.domain

此类型的页面设置了document.domain ,IE不从父级inheritancedocument.domain

为了修复该部分,我将iframe创建更改为标准javascript,并将源设置为在iframe创建时编写document.domain

  var printI= document.createElement('iframe'); printI.name = "printIframe"; printI.id = strFrameName; document.body.appendChild(printI); printI.src = "javascript:document.write('document.domain=\"mydomain.com\";')"; var $iframe = $("#" + strFrameName); 

因此,这修复了拒绝访问,但现在框架将不会打印。 我已经尝试了很多不同的方法来访问对象,但是它们都没有工作。

A)如何在这种情况下访问框架(我已经尝试了SO上概述的大多数方法)以使IE识别和打印

要么

B)任何人都可以想到一个更好的方法来将document.domain放入使用jQuery创建的iframe中吗? (不能在之后,因为访问被拒绝的问题会出现)

在您的代码中,您正在使用setTimeout在iframe加载后执行您的函数。

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

但这是一个错误,因为您不知道给定的时间是否足以加载iframe。 Javascript执行是异步的,所以不能保证setTimeout会在iframe加载之前抵消函数的执行。 由于不同页面的加载时间不同。 有些人无法正确执行代码,指向您发现导致错误的行。

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

正确的方法是使用事件loadonload来了解DOM对象是否已正确加载。

  //or  

只要设置iframe src,就必须针对父元素validation相同的原点,即使您将其设置为“about:blank”。 我猜IE在适当的checkng中失败,或者某些javascript运行并将document.location设置为与创建iframe不同的一个。

如何不设置src,如下所示? 它仍然应该工作。

 var $iframe = $(""); $iframe.appendTo("body"); var $iframeDoc = $iframe[0].contentWindow.document; $iframeDoc.open(); $iframeDoc.write("foo"); $iframeDoc.close(); 

问题是由于IE未inheritance父document.domain。

不幸的是,一旦你进入这个阴暗的区域,需要一些特定的黑客才能让它正常工作。

基本上检查是否正在显式设置document.domain并且浏览器是IE。

完整更新的插件:

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(\"\")"; 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 = $(""); $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("") } }); //add title to iframe if (opt.pageTitle) $doc.find("head").append("" + opt.pageTitle + ""); // import additional stylesheet if (opt.loadCSS) $doc.find("head").append(""); // 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(""); }, 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 $($("
").html(this.clone())).html() } })(jQuery);

IE与所有其他浏览器一样使用iframe(至少对于主要function)。 你只需要保留一套规则:

  • 在iframe中加载任何javascript(需要了解iframe父级的js部分)之前,请确保父级更改了document.domain。
  • 加载所有iframe资源后,将document.domain更改为与parent中定义的相同。 (您需要稍后执行此操作,因为设置域将导致iframe资源的请求失败)

  • 现在你可以为父窗口做一个引用:var winn = window.parent

  • 现在你可以引用父HTML,以便操作它:var parentContent = $(’html’,winn.document)
  • 此时您应该可以访问IE父窗口/文档,您可以随意更改它

这个答案已经在最初的问题更新中说明了,但是我希望为原来的问题添加一个更简洁的答案来解决SCRIPT70权限被拒绝错误(我在使用JQuery 3.2.1的IE11 / Win7上遇到了这个问题)。

而不是$('').appendTo($('body'))

做这个:

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

答案取自这里: https : //bugs.jquery.com/ticket/13936#comment:28