2013-02-14 14 views
11

Jestem autorem printThis, wtyczki jquery do drukowania.jquery - odmowa dostępu iframe w IE na niektórych stronach

https://github.com/jasonday/printThis

Mam użytkownika, który przyniósł aż problem, że nie udało mi się złamać i niestety, nie jestem w stanie udostępnić stronę (dotyczy Privacy).

Na stronie użytkownika problem występuje na niektórych stronach w IE, ale nie na innych. Wydruk nie powiódł się, ponieważ element iframe pozostaje pusty.

Błąd w IE jest w jQuery:

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

Korzystanie rejestrowanie, udało mi się ustalić, to było w przypadku braku wokół tej linii:

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

Ale znowu, to dzieje się tylko na niektórych stronach i nie mogłem odtworzyć w żadnym przypadku poza witryną tego użytkownika.

Moje pytanie: czy jest tu lepsze podejście? lub sposób na uczynienie obiektu $doc bardziej kuloodpornym?


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

UPDATE

Issue aby ze względu na document.domain

Ten typ stronę document.domain zestaw i IE nie dziedziczy document.domain od rodzica.

Aby naprawić tę część, zmieniono tworzenie elementu iframe na standardowy javascript i ustawiono źródło do napisania document.domain w przypadku tworzenia elementu iframe.

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

To rozwiązuje problem odmowy dostępu, ale teraz ramka nie zostanie wydrukowana. Próbowałem wielu różnych metod uzyskiwania dostępu do obiektu, jednak żaden z nich nie działa.

A) w jaki sposób uzyskać dostęp do klatki w tym scenariuszu (próbowałem większość metod przedstawiono na SO), aby uzyskać IE rozpoznać i wydrukować

lub

B) może ktoś pomyśleć lepszy sposób uzyskania document.domain w iframe podczas tworzenia z jQuery? (nie może być później, ponieważ pojawi się problem odmowy dostępu)

+0

Czy wszystkich stron w tej samej domenie/subdomenie? – Christophe

+0

@ Christophe - tak. Wszystkie strony należą do tej samej domeny. Wydruk nie działa specjalnie na jednym typie strony, ale działa na wszystkich innych. – Jason

+1

Może być pomocna: http://stackoverflow.com/questions/364952/jquery-javascript-accessing-contents-of-an-iframe – JSuar

Odpowiedz

3

Problem wynika z faktu, że IE nie dziedziczy dokumentu nadrzędnego document.domain.

Niestety, po wejściu w ten mroczny obszar, trzeba było pewnych specyficznych włamań, aby działało to poprawnie.

Zasadniczo sprawdzanie, czy dokument.domain jest jawnie ustawiony, a przeglądarka to IE.

Pełna zaktualizowane wtyczki:

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

Dopóki ustawisz element iframe src, to samo źródło musi zostać zweryfikowane względem elementu nadrzędnego, nawet jeśli ustawisz go jako "about: blank" . Domyślam się, że IE nie działa poprawnie, lub niektóre javascript działa i ustawia document.location na inny niż iframe.

Co powiesz na NIE ustawianie src w następujący sposób? to nadal powinno działać.

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

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

patrz aktualizacja powyżej. – Jason

+0

Pomogło mi to z podobnym problemem - nie udało mi się uzyskać elementu treści elementu iframe, dopóki nie wykonałem $ ("iframe") [0] .contentWindow.document.open(). – Mark

5

W kodzie używasz setTimeout wykonywać swoją funkcję po załadowaniu iframe.

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

ale jest to błąd, ponieważ nie wiadomo, czy podany czas wystarczy do załadowania elementu iframe, czy nie. Wykonanie Javascript jest asynchroniczne, więc nie ma gwarancji, że setTimeout skompensuje wykonanie tej funkcji do momentu załadowania elementu iframe. Ponieważ czas ładowania jest inny dla różnych stron. Niektóre nie mogą poprawnie wykonać kodu, wskazując linię, która powoduje błędy.

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

Prawidłowe sposobem jest użycie zdarzenia load lub onload poznać, jeśli obiekt DOM został załadowany prawidłowo, czy nie.

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

patrz aktualizacja powyżej. – Jason

+0

chrome nie rozpoznaje zdarzenia onload – Jason

+0

chrome obsługuje je inaczej, patrz tutaj: http://stackoverflow.com/questions/3889173/window-onload-event-fails-in-chrome i https://groups.google. com/forum /? fromgroups = #! topic/django-users/FwLRFzFImVU – user568109

0

IE działa z iframe jak wszystkich innych przeglądarkach (przynajmniej dla głównych funkcji). Po prostu trzeba zachować zestawu reguł:

  • przed załadowaniem jakichkolwiek javascript w iframe (ta część js, który musi wiedzieć o iframe rodzica), należy upewnić się, że rodzic document.domain zmieniło.
  • Po załadowaniu wszystkich zasobów iframe, zmień document.domain na taki sam, jak zdefiniowano w obiekcie nadrzędnym. (Trzeba to zrobić później, ponieważ ustawienie domeny spowoduje żądanie iframe zasobu niepowodzenie)

  • teraz można zrobić odniesienie do okna nadrzędnego: var Winn = window.parent

  • teraz można wnieść odwołanie do macierzystego HTML, aby go manipulować: var parentContent = $ („html”, winn.document)
  • w tym momencie powinieneś mieć dostęp do okna nadrzędnego IE/dokumentu i można go zmienić, jak przyzwyczajenie
+2

Proszę nie kopiować/wklejać tego samego [odpowiedź] (http://stackoverflow.com/questions/1886547/access-is-denied-javascript-error-when-trying-to-access-the-document-object- z/35539021 # 35539021) na wiele pytań. Zamiast tego, każda odpowiedź powinna być dostosowana do konkretnego zadawanego pytania zamiast ogólnej kopii i wklejenia. – Andy

0

Ta odpowiedź została już podana w pierwotnym pytaniu UPDAT E, ale chciałem dodać bardziej zwięzłą odpowiedź na oryginalne pytanie związane z obejściem błędu SCRIPT70 Permission denied (wpadłem na to na IE11/Win7 z JQuery 3.2.1).

Zamiast $('<iframe .../>').appendTo($('body'))

to zrobić:

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

Odbierz wzięty stąd: https://bugs.jquery.com/ticket/13936#comment:28

Powiązane problemy