Sven Rautenberg: IE-DOM vs. Moz-DOM: Property "specified"

Moin!

Ich habe offenbar (soweit ich den Sachverhalt eingrenzen konnte) ein schwerwiegendes Problem mit dem IE-DOM.

Für ein CMS setze ich den WYSIWYG-Editor tinyMCE ein (http://tinymce.moxiecode.com/). Dieser Editor hat die Funktionen, die ich brauche, und obendrein erzeugt er auch noch validen Code, indem er aufgrund vorgebbarer Regeln HTML-Elemente und Attribute erlaubt bzw. löscht, Standard- und Zwangswerte definieren läßt etc.

Mein Problem tritt jetzt auf, weil ich versuche, Formular-Elemente im Editor mit anzeigen zu lassen (von Einfügen wollen wir erstmal gar nicht sprechen).

Die simple Übung ist: Textarea vom serverseitigen Skript befüllen lassen, Editor nach Vorschrift starten lassen (er erzeugt dann ein IFrame zum Bearbeiten), nichts verändern, Speichern-Button klicken.

Der Mozilla verhält sich wie gewünscht: Ich habe z.B. für <input> die Attribute type, name, size und value erlaubt, so dass ein Miniformular mit einem Textfeld (type="text" name="search2" size="6" value="") und einem Submit-Button (type="submit" value="Suchen") unverändert übernommen werden.

Der IE jedoch löscht mir wichtige Attribute:
Das Textfeld behält nur noch die Attribute name und size, der Submitbutton behält nur das type-Attribut.

Im Editorquelltext habe ich das Problem versucht einzugrenzen, und bin dabei auf die folgende Stelle gestoßen:

if (node.attributes.length > 0) {
   for (var i=0; i<node.attributes.length; i++) {
alert("Clean1: "+elementName+"-"+node.attributes[i].nodeName);
      if (node.attributes[i].specified) {
alert("Clean2: "+elementName+"-"+node.attributes[i].nodeName);
         var attrib = tinyMCE._cleanupAttribute(elementValidAttribs, elementName, node.attributes[i], node);
         if (attrib)
            elementAttribs += " " + attrib.name + "=" + '"' + attrib.value + '"';
      }
   }
}

Dieser Teil wird für jedes HTML-Element aufgerufen. In node.attributes stehen alle Attribute des HTML-Elements drin.

Der Mozilla zeigt als Länge des Arrays nur die tatsächlich im Quelltext definierten Attributzahlen an (also 4 und 2 bei meinen INPUTs).

Der IE zeigt als Länge mindestens den Zahlenwert 83 an - er hat sämtliche möglichen Attribute drin, z.B. alle Javascript-Eventhandler. Damit die nicht alle bearbeitet werden, wird die IE-proprietäre Eigenschaft "specified" abgefragt. Die ist angeblich dann true, wenn das Attribut tatsächlich im Dokument angegeben wurde, false dann, wenn Default-Angaben wirksam werden, und kann auch ganz fehlen. Siehe http://www.devguru.com/Technologies/xmldom/quickref/node_specified.html.

Frage an die Experten: Wie kann ich dem IE beibringen, dass ALLE meine im Quelltext definierten Attribute diesen Text mit "true" bestehen und dadurch in meinem Editorquelltext bestehen bleiben?

Wenn ich den Text rausnehme, dann kommen die Attribute nämlich alle "durch" - aber leider werden auch noch Default-Attribute erzeugt (bei <p> und <h1> beispielsweise leere style- und align-Attribute - die kann ich da aber nicht brauchen!).

Wie erwähnt: Im Mozilla funktioniert das alles bestens - was mich irgendwie wundert, aber offenbar hat Mozilla diese specified-Eigenschaft übernommen und im Gegensatz zum IE korrekt umgesetzt.

- Sven Rautenberg

  1. Moin!

    Moin Sven,

    Wie erwähnt: Im Mozilla funktioniert das alles bestens - was mich irgendwie wundert, aber offenbar hat Mozilla diese specified-Eigenschaft übernommen und im Gegensatz zum IE korrekt umgesetzt.

    ich habe im Mozilla DOM-Inspector keine Eigenschaft "specified" gefunden, er kennt sie offenbar nicht! Wenn ich den JS-Code richtig verstehe wird ja gerade an dieser Stelle verzweigt, um die ungesetzten Attribute im IE zu entfernen. Du solltest den Fehler also eher in der Funktion tinyMCE._cleanupAttribute(elementValidAttribs, elementName, node.attributes[i], node) suchen.
    Hier gibts noch ein paar nützliche Infos:
    http://devedge.netscape.com/library/manuals/2000/javascript/1.5/guide/obj2.html#1008342

    • Sven Rautenberg

    cu,
    ziegenmelker

    1. ich habe im Mozilla DOM-Inspector keine Eigenschaft "specified" gefunden, er kennt sie offenbar nicht!

      Ich muß mich korrigieren, die Eigenschaft gibt es sehr wohl!
      cu,
      ziegenmelker

    2. Moin!

      Danke schonmal für die "zweite Meinung".

      Wie erwähnt: Im Mozilla funktioniert das alles bestens - was mich irgendwie wundert, aber offenbar hat Mozilla diese specified-Eigenschaft übernommen und im Gegensatz zum IE korrekt umgesetzt.
      ich habe im Mozilla DOM-Inspector keine Eigenschaft "specified" gefunden, er kennt sie offenbar nicht!

      Irgendwer oder irgendwas liefert aber in node.attributes[i].specified auch im Mozilla true zurück. Der JS-Code setzt diese Eigenschaft jedenfalls nicht, es gibt nur zwei Stellen in der Funktion, an der auf diese Eigenschaft lesend zugegriffen wird.

      Dass diese Eigenschaft in mehr als nur dem IE existiert, scheint http://www.quirksmode.org/dom/w3c_core.html zu bestätigen. Und die Testseite http://www.quirksmode.org/dom/tests/attributes.html# hat oben zwei Testelemente und ganz unten zwei Testlinks. Der zweite (mit Zahlenindex) funktioniert bei mir im Opera 7.5 und gibt true zurück, der erste funktioniert dort nicht.

      Wenn ich den JS-Code richtig verstehe wird ja gerade an dieser Stelle verzweigt, um die ungesetzten Attribute im IE zu entfernen. Du solltest den Fehler also eher in der Funktion tinyMCE._cleanupAttribute(elementValidAttribs, elementName, node.attributes[i], node) suchen.

      Diese Funktion wird sowohl vom IE als auch vom Mozilla durchlaufen, bei beiden ist die Eigenschaft "specified" bei manchen Attributen "true" - beim IE nur bei zu wenig Attributen.

      Hier gibts noch ein paar nützliche Infos:
      http://devedge.netscape.com/library/manuals/2000/javascript/1.5/guide/obj2.html#1008342

      Danke - aber ich habe ein Problem mit dem IE. Vielleicht fällt dir oder jemand anderem ja noch was Schlaues zu dem Problem ein.

      - Sven Rautenberg

  2. Hallo Sven.

    Dieser Teil wird für jedes HTML-Element aufgerufen. In node.attributes stehen alle Attribute des HTML-Elements drin.

    Genau da ist der Fehler. Laut Dokumentation http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/specified.asp enthält node.attributes im IE _alle_ für dieses Element möglichen Attribute. Und genau deshalb dürfte im IE das falsche Ergebnis rauskommen (irgendein Attribut ist immer false).

    Wie kann ich dem IE beibringen, dass ALLE meine im Quelltext definierten Attribute diesen Test mit "true" bestehen und dadurch in meinem Editorquelltext bestehen bleiben?

    Dir wird nichts anderes übrigbleiben, als das Array Element für Element durchzugehen und nur diejenigen Attribute abzufragen und ggf. zu extrahieren, die du brauchst. Zugriff auf den Attributnamen hast du bekanntlich mit nodeName, auf den gesetzten Wert mit nodeValue. Ich habe mal spaßeshalber das Beispiel der oben verlinkten Seite hochgeladen, vielleicht hilft dir das weiter: http://test.anaboe.net/specified.html (ergibt natürlich nur beim Aufruf im IE einen Sinn). Es sieht jedenfalls so aus, als ob es bestimmte Attribute gibt, die der IE als "required" einstuft, und deshalb je nachdem, ob ein Wert gesetzt wurde oder nicht, 'false' oder 'true' zurückgibt, denn in allen anderen Fällen (auch bei Eventhandlern) liefert er 'null'.

    Du kannst den Test ja mal im Hinblick auf dein zu untersuchendes HTML-Element umschreiben, vielleicht hilft dir das irgendwie weiter. Ansonsten bleibt dir wie gesagt wohl nur die oben genannte Möglichkeit, denn das Verhalten 'null' oder 'false' scheint mir ein wenig willkürlich zu sein. Aber vielleicht weiß jemand noch mehr :)

    Freundschaft!
    Siechfred

    --
    Wissen bedeutet zu wissen, wo es steht.
    1. Hallo Sven.

      Ergänzend zum Code von Thomas noch eine zweite Variante von mir, die alle unerwünschten Attribute weglässt:

      http://test.anaboe.net/specified_v2.html

      Freundschaft!
      Siechfred

      --
      Wissen bedeutet zu wissen, wo es steht.
  3. Hallo,

    Frage an die Experten: Wie kann ich dem IE beibringen, dass ALLE meine im Quelltext definierten Attribute diesen Text mit "true" bestehen und dadurch in meinem Editorquelltext bestehen bleiben?

    Hier mal ein kleiner Test, der alle fuer den BODY im Quellcode definierten Attribute ausgibt.

    ...
    <body id="bd" bgcolor="#FFFFFF" text="#000000">
    <script language="JavaScript" type="text/javascript">
    <!--

    var node,attr,l;
    node=document.getElementById("bd");
    attr=node.attributes;
    l=attr.length;
    for(i=0;i<l;i++)
    if(attr[i].specified)document.write(attr[i].name+" : "+attr[i].value+"<br>");

    //-->
    </script>
    </body>
    ...

    • prinzipiell gleiches Ergebnis unter IE 6.0, Firefox 0.9.2, Opera 7.50:

    text : #000000
    bgcolor : #ffffff
    id : bd

    Nur die Schreibweise differiert etwas (z. B. bgColor, bgcolor, BGCOLOR -- also ggf. noch toLowerCase() anwenden.

    Vielleicht laesst sich das in den Code integrieren.

    MfG, Thomas

    1. Moin!

      Hier mal ein kleiner Test, der alle fuer den BODY im Quellcode definierten Attribute ausgibt.

      Der Test ist gut. Und so harmlos auch im IE, wenn man die "richtigen" HTML-Elemente verwendet.

      Aber wenn man die "falschen" nimmt, versagt der IE offenbar.

      Ich habe deinen Testcase mal etwas aufgebohrt:

      ...
      <body id="bd" bgcolor="#FFFFFF" text="#000000">
      <input type="submit" name="hallo" value="+"><br>
      <input type="text" name="werda" size="10" value="defaultbelegung"><br>
      <input type="radio" name="sender" value="test"><br>
      <script language="JavaScript" type="text/javascript">
      <!--

      var node,attr,l;
      node=document.getElementsByTagName("input");
      for (j=0; j<node.length; j++)
      {
        attr=node[j].attributes;
        l=attr.length;
        for(i=0;i<l;i++) {
          if(attr[i].specified) {
            document.write(attr[i].name+" ::: "+attr[i].value+"<br>");
          }
          else {
      //      document.write(attr[i].name+" - "+attr[i].value+"<br>");
          }
        }
        document.write("<hr>");
      }
      //-->
      </script>
      </body>
      ...

      Resultat:
      Mozilla, Opera zeigen alle in den Inputs gesetzten Attribute an, IE nicht!

      Das type-Attribut wird nicht angezeigt beim Textfeld, und das value-Attribut wird generell nicht angezeigt.

      Nur die Schreibweise differiert etwas (z. B. bgColor, bgcolor, BGCOLOR -- also ggf. noch toLowerCase() anwenden.

      Die Schreibweise wäre das kleinere Problem.

      Ich denke, ich werde diesen Effekt mit einer Sonderbehandlung umgehen müssen: if nodeName="input" then ... *seufz*

      - Sven Rautenberg

      1. Hallo Sven.

        Hm, scheinbar hat dich mein Ansatz nicht überzeugt :-/

        if(attr[i].specified)

        Diese Zeile dürfte dein Problem sein. Schreibst du statt dessen if(attr[i].nodeValue), bekommst du alle definierten Attribute. Allerdings sind ein paar dabei, für die der IE scheinbar eine standardmäßige Vorbelegung hat. Ändere ich deinen Code, ergibt das folgende Ausgabe:

        contentEditable ::: inherit
        start ::: fileopen
        type ::: submit
        height ::: 0
        maxLength ::: 2147483647
        loop ::: 1
        size ::: 20
        value ::: +
        name ::: hallo

        --------------------------------------------------------------------------------
        contentEditable ::: inherit
        start ::: fileopen
        type ::: text
        height ::: 0
        maxLength ::: 2147483647
        loop ::: 1
        size ::: 10
        value ::: defaultbelegung
        name ::: werda

        --------------------------------------------------------------------------------
        contentEditable ::: inherit
        start ::: fileopen
        type ::: radio
        height ::: 0
        maxLength ::: 2147483647
        loop ::: 1
        size ::: 20
        value ::: test
        name ::: sender

        Ich denke, ich werde diesen Effekt mit einer Sonderbehandlung umgehen müssen: if nodeName="input" then ... *seufz*

        Sehe ich nicht ganz so. Wie du oben siehst, bekommst du alles, was du brauchst, du musst nur die von dir gesuchten Werte extrahieren und den Rest ignorieren.

        Freundschaft!
        Siechfred

        --
        Wissen bedeutet zu wissen, wo es steht.
        1. Hi,

          so könnte man es dann zusammenfassen:
          <body id="bd" bgcolor="#FFFFFF" text="#000000">
          <input type="submit" name="hallo" value="+"><br>
          <input type="text" name="werda" size="10" value="defaultbelegung"><br>
          <input type="radio" name="sender" value="test"><br>
          <script language="JavaScript" type="text/javascript">
          <!--

          var node,attr,l;
          node=document.getElementsByTagName("input");
          for (j=0; j<node.length; j++)
          {
            attr=node[j].attributes;
            l=attr.length;
            for(i=0;i<l;i++) {
              if(attr[i].nodeValue  && attr[i].value!="" && attr[i].value!="inherit" && attr[i].value!="false" && attr[i].value!="0" && attr[i].name!="start" && attr[i].name!="maxLength" && attr[i].name!="loop") {
                document.write(attr[i].name+" ::: "+attr[i].value+"<br>");
              }
              else {
          //      document.write("----------"+attr[i].name+" - "+attr[i].value+"<br>");
              }
            }
            document.write("<hr>");
          }
          //-->
          </script>
          </body>
          ergibt:
          type ::: submit
          size ::: 20
          value ::: +
          name ::: hallo

          --------------------------------------------------------------------------------
          type ::: text
          size ::: 10
          value ::: defaultbelegung
          name ::: werda

          --------------------------------------------------------------------------------
          type ::: radio
          size ::: 20
          value ::: test
          name ::: sender

          Die size würde ich nicht rauswerfen, die möchtest du ja vielleicht auch setzen.

          cu,
          ziegenmelker

          1. Hallo,

            document.write(attr[i].name+" ::: "+attr[i].value+"<br>");

            name und value sind im MSIE 5.0 undefined.

            Mathias

      2. Hallo,

        Das type-Attribut wird nicht angezeigt beim Textfeld, und das value-Attribut wird generell nicht angezeigt.

        Dieser Workaround bezogen auf mein Beispiel:

        if(attr[i].specified || attr[i].name=="type" || attr[i].name=="value")document.write(attr[i].name+" : "+attr[i].value+"<br>");

        ist zumindest im IE 6.0 ein (weiterer) Ansatz.

        MfG, Thomas

  4. Hallo,

    Der IE zeigt als Länge mindestens den Zahlenwert 83 an - er hat sämtliche möglichen Attribute drin, z.B. alle Javascript-Eventhandler. Damit die nicht alle bearbeitet werden, wird die IE-proprietäre Eigenschaft "specified" abgefragt. Die ist angeblich dann true, wenn das Attribut tatsächlich im Dokument angegeben wurde, false dann, wenn Default-Angaben wirksam werden, und kann auch ganz fehlen.

    http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-862529273

    Frage an die Experten: Wie kann ich dem IE beibringen, dass ALLE meine im Quelltext definierten Attribute diesen Text mit "true" bestehen und dadurch in meinem Editorquelltext bestehen bleiben?

    Die Funktion fängt so an:

    function TinyMCE__cleanupAttribute(valid_attributes, element_name, attribute_node, element_node) {
     var attribName = attribute_node.nodeName.toLowerCase();
     var attribValue = attribute_node.nodeValue;
     ...

    Damit ist das ganze Unternehmen schon zum Scheitern verurteilt, weil nodeValue bei (gesetzen!) name- und value-Attributen im MSIE 5.0 null ist (denke mal, es ist im 5.5 und 6 genauso oder String('')). Glücklicherweise funktioniert getAttribute:

    var elementValidAttribs = ["type", "name", "id", "value", "size"];
    Array.prototype.contains = function (str) { // siehe Archiv...
     str = str.toLowerCase();
      var i, exists = false;
     for (i = 0; i < this.length; i++) {
      if (this[i] === str) {
              exists = true;
              break;
      }
     }
     return exists;
    };

    var elem = document.createElement("input");
    elem.setAttribute("type", "text");
    elem.setAttribute("name", "search2");
    elem.setAttribute("size", "6");
    elem.setAttribute("value", "bla");

    var i, attrName, attrValue, elementAttribs = new String();

    for (i = 0; i < elem.attributes.length; i++) {
     attrName = elem.attributes[i].nodeName;
     attrValue = elem.getAttribute(attrName);
     document.write("<p>Attribut: " + attrName + "<br>" +
      "specified: " + elem.attributes[i].specified + "<br>" +
      "nodeValue: '" + elem.attributes[i].nodeValue + "'<br>" +
      "nodeValue: '" + elem.attributes[i].nodeValue + "'<br>" +
      "contains: " + elementValidAttribs.contains(attrName) + "<br>" +
      "getAttribute: '" + attrValue + "'</p>");
            if (attrValue !== null && attrValue !== "" && elementValidAttribs.contains(attrName)) {
      elementAttribs += " " + attrName + '="' + attrValue + '"';
     }
    }

    document.write("<p>&lt;" + elem.nodeName + elementAttribs + "&gt;</p>")

    Alle Attribute mit null und '' kannst du einfach überspringen, oder sehe ich das falsch? Ansonsten einfach die Bedingungen !==null und !=='' löschen.

    Mathias

    1. Hallo molily.

      Damit ist das ganze Unternehmen schon zum Scheitern verurteilt, weil nodeValue bei (gesetzen!) name- und value-Attributen im MSIE 5.0 null ist (denke mal, es ist im 5.5 und 6 genauso oder String('')).

      nodeValue liefert im IE 6 einen Leerstring, dass es allerdings im IE 5 bzw. möglicherweise auch im IE 5.5 null ergibt, war mir nicht bekannt. Zumindest hier im IE 6 geht dein Beispiel auch dann, wenn ich statt

      attrValue = elem.getAttribute(attrName);

      attrValue = elem.attributes[i].nodeValue;

      schreibe. Nur so zur Ergänzung :)

      Freundschaft!
      Siechfred

      --
      Wissen bedeutet zu wissen, wo es steht.
  5. Moin!

    Für den Vorher/Nachher-Vergleich:

    if (node.attributes.length > 0) {
       for (var i=0; i<node.attributes.length; i++) {
          if (node.attributes[i].specified) {
             var attrib = tinyMCE._cleanupAttribute(elementValidAttribs, elementName, node.attributes[i], node);
             if (attrib)
                elementAttribs += " " + attrib.name + "=" + '"' + attrib.value + '"';
          }
       }
    }

    if (node.attributes.length > 0) {
          for (var i=0; i<node.attributes.length; i++) {
             attrName = node.attributes[i].nodeName;
             attrValue = node.getAttribute(attrName);
             if (attrValue !== null && attrValue !== "" && typeof(attrValue) != "object") {
                var attrib = tinyMCE._cleanupAttribute(elementValidAttribs, elementName, node.attributes[i], node);
                if (attrib)
                   elementAttribs += " " + attrib.name + "=" + '"' + attrib.value + '"';
             }
          }
       }

    Das ist eine Mischung aus molilys Vorschlägen (nicht dass ich die Kompatibilität zu IE5 brauchen würde - aber man weiß ja nie) und meinen konkreten Anforderungen. Durch das Ausschließen von "object"-Attributen (das ist nach meinen Erkenntnissen nur das style-Attribut) wird _mein_ Quelltext wie gewünscht behandelt. Die Funktion _cleanupAttribute() erledigt den Rest: Sie hat durch die Konfiguration des Editors erfahren, welche Attribute überhaupt nur zulässig sind, und filtert alle anderen Attribute (aber eben nicht style - warum auch immer) heraus.

    Danke an alle, die sich um eine Lösung bemüht haben.

    - Sven Rautenberg