Externe Scripts nachladen (auch mit document.write)
Anzeige Hier werben
Für viele Dienste (Werbung, Statistik) muss man Scripte eines Anbieters auf seine Seite einbinden. Wenn der Server, von dem dieser Code nachgeladen wird, laggt, dann friert die gesamte Webseite ein und wartet bis das externe Script geladen ist.
Weil
diese Scripte oft document.write()-Befehle enthalten kann man den
Code aber nicht einfach per Ajax später nachladen und per eval()
ausführen: document.write() funktioniert nur während der Browser die Webseite aufbaut. Wenn diese Seite schon aufgebaut ist, wird dadurch die
gesamte Seite zerschossen.
Hier die Lösung für dieses Problem:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | var domWrite = (function(){ // by Frank Thuerigen
// private
var dw = document.write, // save document.write()
myCalls = [], // contains all outstanding Scripts
t = ''; // timeout
function startnext(){ // start next call in pipeline
if ( myCalls.length > 0 ) {
if ( Object.watch ) console.log( 'next is '+myCalls[0].f.toString() );
myCalls[0].startCall();
}
}
function evals( pCall ){ // eval embedded script tags in HTML code
var scripts = [],
script,
regexp = /<script[^>]*>([\s\S]*?)<\/script>/gi;
while ((script = regexp.exec(pCall.buf))) scripts.push(script[1]);
scripts = scripts.join('\n');
if (scripts) {
eval(scripts);
}
}
function finishCall( pCall ){
pCall.e.innerHTML = pCall.buf; // write output to element
evals( pCall );
document.write=dw; // restore document.write()
myCalls.shift();
window.setTimeout( startnext, 50 );
}
function testDone( pCall ){
var myCall = pCall;
return function(){
if ( myCall.buf !== myCall.oldbuf ){
myCall.oldbuf = myCall.buf;
t=window.setTimeout( testDone( myCall ), myCall.ms );
}
else {
finishCall( myCall );
}
}
}
function MyCall( pDiv, pSrc, pFunc ){ // Class
this.e = ( typeof pDiv == 'string' ?
document.getElementById( pDiv ) :
pDiv ), // the div element
this.f = pFunc || function(){},
this.stat = 0, // 0=idle, 1=waiting, 2=running, 3=finished
this.src = pSrc, // script source address
this.buf = '', // output string buffer
this.oldbuf = '', // compare buffer
this.ms = 100, // milliseconds
this.scripttag; // the script tag
}
MyCall.prototype={
startCall: function(){
this.f.apply( window ); // execute settings function
this.stat=1;
var that = this; // status = waiting
document.write = (function(){
var o=that,
cb=testDone( o ),
t;
return function( pString ){ // overload document.write()
window.clearTimeout( t );
o.stat=2; // status = running
window.clearTimeout(t);
o.oldbuf = o.buf;
o.buf += pString; // add string to buffer
t=window.setTimeout( cb, o.ms );
};
})();
var s=document.createElement('script');
s.setAttribute('language','javascript');
s.setAttribute('type','text/javascript');
s.setAttribute('src', this.src);
document.getElementsByTagName('head')[0].appendChild(s);
}
}
return function( pDiv, pSrc, pFunc ){ // public
var c = new MyCall( pDiv, pSrc, pFunc );
myCalls.push( c );
if ( myCalls.length === 1 ){
startnext();
}
}
})();
|
Als Datei zum einbinden in die eigene Seite kann man das hier herunterladen.
Erklärung
Die Funktionalität ist prinzipiell recht einfach, aber im Detail sehr vertrackt...
- Wegen der "same origin policy" (AJAX requests dürfen nur vom selben Server aufgerufen werden) muß ein Skript von einem fremden Server erst als script node erzeugt werden und dann in den HEAD der Seite eingesetzt werden.
- Bevor dies geschieht, muß die "document.write()" Funktion in eine temporäre Variable gesichert werden, danach wird "document.write()" mit einer eigenen Pufferfunktion überschrieben.
- Die Pufferfunktion erhält den String, der normalerweise direkt beim rendern geschrieben würde. Dieser wird in der Puffervariable "buf" angehängt. Außerdem wird bei jedem Aufruf ein "timeout()" neu gestartet, der prüft ob sich in den letzten 100ms "buf" verändert hat. Falls nicht wird "buf" in dem DIV gespeichert, das man bei Aufruf der Funktion mitgegeben hat.
- Ganz am Schluß wird die originale document.write() Funktion aus der Sicherung wiederhergestellt.
- Zum Abarbeiten mehrerer simultaner Aufrufe der Funktion wurde ein Script Queue hinzugefügt. Das ganze gleicht einer asynchronen Batch-Verarbeitung.
Beispielanwendung mit Google Adsense
Für google adsense sieht der HTML code etwa so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <div id="googlesidebar1"></div>
<script type="text/javascript">
domWrite(
'googlesidebar1',
'http://pagead2.googlesyndication.com/pagead/show_ads.js',
function(){
google_ad_client = "pub-YourOwnGoogleCode";
google_ad_slot = "YourOwnGoogleCode";
google_ad_width = 160;
google_ad_height = 600;
}
);
</script>
|
ACHTUNG: ich kann nicht garantieren, daß google das erlaubt. Es sollte problemlos sein da der google adsense code in keiner Weise verändert wird. Trotzdem macht das jeder auf seine eigene Verantwortung!
Hier noch mal eine Seite wo man das ganze in Aktion sehen kann... die Werbung in der rechten Sidebar unter den Menüs ( 2 Blöcke ) und im Footer der Seite ( 1 Block ) wurde auf diese Weise asynchron und simultan nachgeladen:
Weiterführende Informationen insbesondere in Bezug auf XHTML und document.write() gibt es auch noch (englisch) bei John Resigs Blog
Ich hoffe das hilft dem einen oder anderen...
Grüße, Frankie



IE7
Leider wird die Werbung im IE7 nicht nachgeladen. IE8 konnte ich bis jetzt noch nicht überprüfen.