molily: Prototypes Element.repace

Beitrag lesen

Hallo,

ich glaube, dass ist nicht richtig, was Du geschrieben hast.
Prototype führt sowas nicht mit einem Timeout aus.

Du glaubst? Ich kann dir gerne den Prototype-Quelltext vorlesen.

Element.replace:

Element.Methods = {  
...  
  replace: function(element, content) {  
    element = $(element);  
    if (content && content.toElement) content = content.toElement();  
    else if (!Object.isElement(content)) {  
      content = Object.toHTML(content);  
      var range = element.ownerDocument.createRange();  
      range.selectNode(element);  
      content.evalScripts.bind(content).defer();  
      content = range.createContextualFragment(content.stripScripts());  
    }  
    element.parentNode.replaceChild(content, element);  
    return element;  
  },  
...

Man beachte hier die Zeile
content.evalScripts.bind(content).defer();

bind erzeugt nur eine Wrapper-Funktion, die evalScripts im Kontext von content ausführt. (Wobei dieser Aufruf an dieser Stelle überflüssig ist, denke ich.)
content ist die String-Variable mit HTML-Code und darin script-Elemente.

evalScripts:

Object.extend(String.prototype, {  
...  
  evalScripts: function() {  
    return this.extractScripts().map(function(script) { return eval(script) });  
  },  
...

Was extractScripts und map machen, ist ja offensichtlich, also ist noch defer interessant:

Function.prototype.defer = Function.prototype.delay.curry(0.01);

curry(0.01) gibt eine Funktion zurück, bei der defer mit dem Parameter 0.01 aufgerufen wird (nennt sich Currying).

Schließlich zu delay:

Object.extend(Function.prototype, {  
...  
  delay: function() {  
    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;  
    return window.setTimeout(function() {  
      return __method.apply(__method, args);  
    }, timeout);  
  },  
...

Hier wird also die Funktion mit setTimeout aufgerufen. Und wenn delay über defer aufgerufen wird, dann ist der zweite Parameter von setTimout 1 für eine Millisekunde.

Das ist alles ziemlich konsequente funktionale Programmierung auf Basis von prototypischer Erweiterung, aber letztlich ist der Zweck dieser Konstrukte, dass evalScripts zeitverzögert ausgeführt wird. Wenn ich mich da irre, zeige es mir bitte auf.

Den Sinn von so etwas wie defer erfasst man, wenn man weiß, How JavaScript Timers Work.

  1. Kann man kein Javascript dynamisch in das DOM "injizieren". Das ist so, als würde man einDiv.innerHTML = "<script>aaa=function (alert(1))</script>"; machen wollen. Das interessiert den Browser überhaupt nicht.

Es geht hier um Element.replace und das ist etwas ganz anderes als eine Zuweisung an innerHTML. Genauer gesagt wird innerHTML überhaupt nicht verwendet, wie man an obigem Code sieht. Und zu den Scripten siehe Dokumentation:
"If it [i.e. der Parameter html] contains any <script> tags, these will be evaluated after element has been replaced (Element.replace() internally calls String#evalScripts)."
Aber auch das kann man im Quelltext sehen.

Mathias