2013-02-14 42 views
11

Tôi là tác giả của printThis, một plugin jquery để in.jquery - truy cập iframe bị từ chối trong IE trên một số trang

https://github.com/jasonday/printThis

Tôi có một người sử dụng đã được đưa lên một vấn đề, mà tôi đã không thể để crack và không may, tôi không thể chia sẻ trang (mối quan tâm riêng tư).

Trên trang web của người dùng, sự cố hiển thị trên một số trang trong IE, nhưng không hiển thị trên các trang khác trong IE. Bản in sẽ không xảy ra vì khung nội tuyến vẫn trống.

Các lỗi trong IE là trong jQuery:

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

Sử dụng khai thác gỗ, tôi đã có thể xác định nó đã thất bại xung quanh dòng này:

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

Nhưng một lần nữa, điều này chỉ xảy ra trên một số trang và tôi không thể tạo lại trong bất kỳ trường hợp nào bên ngoài trang web của người dùng này.

Câu hỏi của tôi: Có cách tiếp cận nào tốt hơn ở đây không? hoặc một phương pháp để làm cho đối tượng $doc chống đạn hơn?


// ----------------------------------------------------------------------- 
// 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); 

CẬP NHẬT

Issue để do document.domain

Đây là loại trang có document.domain bộ và IE không kế thừa document.domain từ cha mẹ.

Để sửa phần đó, tôi đã thay đổi việc tạo khung nội tuyến thành javascript chuẩn và đặt nguồn để viết document.domain khi tạo khung nội tuyến.

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); 

Vì vậy, điều này sẽ khắc phục quyền truy cập bị từ chối, tuy nhiên khung này sẽ không in. Tôi đã thử rất nhiều phương pháp khác nhau để truy cập đối tượng, tuy nhiên không có phương pháp nào trong số chúng hoạt động.

A) làm thế nào bạn sẽ truy cập vào khung trong kịch bản này (Tôi đã thử hầu hết các phương pháp nêu trên SO) để có được IE để nhận ra và in

hoặc

B) bất cứ ai có thể nghĩ đến một cách tốt hơn để có được document.domain vào khung nội tuyến khi tạo bằng jQuery? (không thể sau đó, vì vấn đề truy cập bị từ chối sẽ xuất hiện)

+0

Có phải tất cả các trang trong cùng một tên miền/tên miền phụ? – Christophe

+0

@Christophe - vâng. Tất cả các trang đều nằm trong cùng một miền. Bản in không hoạt động cụ thể trên một loại trang, nhưng hoạt động trên tất cả các trang khác. – Jason

+1

Có thể hữu ích: http://stackoverflow.com/questions/364952/jquery-javascript-accessing-contents-of-an-iframe – JSuar

Trả lời

3

Vấn đề là do IE không kế thừa parent.domain gốc.

Thật không may, một khi bạn vào khu vực u ám này, phải mất một số hacks cụ thể để có được điều này để hoạt động đúng.

Về cơ bản, hãy kiểm tra xem document.domain có được đặt rõ ràng không và trình duyệt là IE.

Full Plugin Cập nhật:

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); 
4

Miễn là bạn đặt iframe src, cùng một nguồn gốc phải được xác minh dựa vào phần tử cha, ngay cả khi bạn đặt nó là 'about: blank' . Tôi đoán IE thất bại trong checkng thích hợp, hoặc một số javascript chạy và thiết lập document.location đến một khác với khung nội tuyến được tạo ra.

Làm thế nào để KHÔNG đặt src như sau? nó vẫn hoạt động.

var $iframe = $("<iframe id='" + strFrameName +"'/>"); 
$iframe.appendTo("body"); 
var $iframeDoc = $iframe[0].contentWindow.document; 

$iframeDoc.open(); 
$iframeDoc.write("foo"); 
$iframeDoc.close(); 
+0

xem cập nhật ở trên. – Jason

+0

Điều này đã giúp tôi với một vấn đề tương tự - tôi đã không thể lấy phần tử nội tuyến iframe cho đến khi tôi thực hiện $ ('iframe') [0] .contentWindow.document.open(). – Mark

5

Trong mã của bạn, bạn đang sử dụng setTimeout để thực thi hàm sau khi khung nội tuyến đã tải.

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

nhưng đây là một sai lầm khi bạn không biết liệu đã đủ thời gian để tải iframe hay chưa. Thực thi Javascript là không đồng bộ, do đó, không có đảm bảo rằng setTimeout sẽ bù đắp việc thực thi hàm cho đến khi tải iframe. Vì thời gian tải khác nhau cho các trang khác nhau. Một số không thể thực thi mã đúng cách, chỉ vào dòng mà bạn thấy là gây ra lỗi.

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

Cách đúng là sử dụng sự kiện load hoặc onload để tìm hiểu nếu đối tượng DOM đã được nạp đúng cách hay không.

<script> 
document.getElementById("myframe").onload = function() { 
    alert("myframe is loaded"); 
}; 
</script> 
//or 
<iframe id="myFrame" onload="myFunction();"></iframe> 
+0

xem cập nhật ở trên. – Jason

+0

chrome không nhận ra sự kiện onload – Jason

+0

chrome xử lý nó theo cách khác, xem tại đây: http://stackoverflow.com/questions/3889173/window-onload-event-fails-in-chrome và https://groups.google. com/forum /? fromgroups = #! topic/django-users/FwLRFzFImVU – user568109

0

IE làm việc với iframe giống như tất cả các trình duyệt khác (ít nhất là cho các chức năng chính). Bạn chỉ cần giữ một bộ quy tắc:

  • trước khi bạn tải bất kỳ javascript nào vào iframe (phần đó cần biết về cha mẹ iframe), đảm bảo rằng cha mẹ có document.domain đã thay đổi.
  • khi tất cả tài nguyên khung nội tuyến được tải, hãy thay đổi document.domain thành giống như tài liệu được xác định trong nguồn gốc. (Bạn cần phải làm điều này sau đó vì đặt tên miền sẽ gây ra yêu cầu tài nguyên iframe của thất bại)

  • bây giờ bạn có thể làm cho một tài liệu tham khảo cho cửa sổ cha mẹ: var Winn = window.parent

  • bây giờ bạn có thể làm cho một tài liệu tham khảo đối với HTML mẹ, để thao tác với nó: var parentContent = $ ('html', winn.document)
  • vào thời điểm này, bạn phải có quyền truy cập vào cửa sổ/tài liệu mẹ của IE và bạn có thể thay đổi nó theo cách bạn muốn
+2

Vui lòng không sao chép/dán cùng một [answer] (http://stackoverflow.com/questions/1886547/access-is-denied-javascript-error-when-trying-to-access-the-document-object-/35539021 # 35539021) cho nhiều câu hỏi. Thay vào đó, mỗi câu trả lời phải được tùy chỉnh cho câu hỏi cụ thể được hỏi thay vì sao chép và dán chung. – Andy

0

Câu trả lời này đã được nêu trong câu hỏi gốc UPDAT E, nhưng tôi muốn thêm một câu trả lời ngắn gọn hơn cho câu hỏi ban đầu liên quan đến việc xung quanh các giấy phép SCRIPT70 bị từ chối lỗi (tôi chạy vào điều này trên IE11/Win7 với JQuery 3.2.1).

Thay vì $('<iframe .../>').appendTo($('body'))

Đừng này:

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

trả lời lấy từ đây: https://bugs.jquery.com/ticket/13936#comment:28

Các vấn đề liên quan