我是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 = $("<iframe id='" + strFrameName +"' src='about:blank'/>");
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("<link type='text/css' rel='stylesheet' href='" + href + "' media='"+media+"'>");
}
});
}
// add another stylesheet
if (opt.loadCSS)
{
$doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + opt.loadCSS + "'>");
}
//add title of the page
if (opt.titlePage)
{
$doc.find("head").append('<title>'+opt.titlePage+'</title>');
}
//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 $($('<div></div>').html(this.clone())).html();
}
})(jQuery);
更新
问题归于document.domain
此类页面设置为document.domain
,IE不会从父级继承document.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('<head><script>document.domain=\"mydomain.com\";</script></head><body></body>')";
var $iframe = $("#" + strFrameName);
因此,这修复了拒绝访问,但现在框架将不会打印。我已经尝试了很多不同的方法来访问该对象,但是它们都没有工作。
A)如何在这种情况下访问框架(我已经尝试了SO上概述的大多数方法)以使IE识别并打印
或
B)任何人都可以想到一个更好的方法来将document.domain放入使用jQuery创建的iframe中吗? (不能随后,因为访问被拒绝的问题会出现)
答案 0 :(得分:6)
在您的代码中,您使用setTimeout
在iframe加载后执行您的功能。
// allow iframe to fully render before action
setTimeout ( function () {
...
}, 333 ); //333ms
但这是一个错误,因为您不知道给定的时间是否足以加载iframe。 Javascript执行是异步的,所以不能保证setTimeout
会在iframe加载之前抵消函数的执行。由于不同页面的加载时间不同。有些人无法正确执行代码,指向您发现导致错误的行。
var $doc = $("#" + strFrameName).contents(); //only after loading
正确的方法是使用事件load
或onload
来了解DOM对象是否已正确加载。
<script>
document.getElementById("myframe").onload = function() {
alert("myframe is loaded");
};
</script>
//or
<iframe id="myFrame" onload="myFunction();"></iframe>
答案 1 :(得分:5)
只要设置了iframe src,就必须针对父元素验证相同的原点,即使您将其设置为“about:blank”。我猜IE在适当的检查中失败,或者运行了一些javascript并将document.location设置为与创建iframe不同的文件。
如何不设置src,如下所示?它仍然应该工作。
var $iframe = $("<iframe id='" + strFrameName +"'/>");
$iframe.appendTo("body");
var $iframeDoc = $iframe[0].contentWindow.document;
$iframeDoc.open();
$iframeDoc.write("foo");
$iframeDoc.close();
答案 2 :(得分:4)
问题是由于IE没有继承父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(\"<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);
答案 3 :(得分:0)
IE与所有其他浏览器一样使用iframe(至少对于主要功能)。你只需要保留一套规则:
加载所有iframe资源后,将document.domain更改为与parent中定义的相同。 (您需要稍后执行此操作,因为设置域将导致iframe资源的请求失败)
现在你可以为父窗口做一个参考:var winn = window.parent
答案 4 :(得分:0)
这个答案已经在最初的问题更新中说明了,但是我希望为原来的问题添加一个更简洁的答案来解决SCRIPT70 Permission denied错误(我在IE11 / Win7上使用JQuery 3.2.1遇到了这个问题) )。
而不是$('<iframe .../>').appendTo($('body'))
这样做:
var $iframe = $('<iframe .../>');
document.body.appendChild($iframe[0]);