Andreas Dölling: RegExp: Merkwürdiges Verhalten des IE bei replace

Hallo,

ich habe folgendes Problem: ich möchte den Inhalt eines HTML-Container-Elements auslesen und per POST-Formular zur Weiterverarbeitung versenden.
Den HTML-Inhalt hole ich mir mit containerElement.innerHTML();

Im Mozilla/Firefox ist das überhaupt kein Problem, der IE dagegen liefert als innerHTML nicht valides HTML, d.h. die Elementnamen sind plötzlich Großbuchstaben und die Standardattribut-Werte sind nicht mehr in Anführungszeichen eingeschlossen, also z.B.: <P class=test>

Nun gibt es ja zum Glück reguläre Ausdrücke, dachte ich mir und wollte daher im ersten Schritt die Elementnamen wieder in kleingeschriebene Strings verwandeln:
ieValue = ieValue.replace(/<([A-Z]+)([^>]*?)>/ig, "$1".toLowerCase());

Der Ausdruck ist nicht sonderlich komplex und matcht auch.
Allerdings weigert sich der IE, die gefundenen Elementnamen durch die kleingeschriebene Version ("$1".toLowerCase()) zu ersetzen.

Testweise habe ich es einmal mit dem Ausdruck:
Hallo,

ich habe folgendes Problem: ich möchte den Inhalt eines HTML-Container-Elements auslesen und per POST-Formular zur Weiterverarbeitung versenden.
Den HTML-Inhalt hole ich mir mit containerElement.innerHTML();

Im Mozilla/Firefox ist das überhaupt kein Problem, der IE dagegen liefert als innerHTML nicht valides HTML, d.h. die Elementnamen sind plötzlich Großbuchstaben und die Standardattribut-Werte sind nicht mehr in Anführungszeichen eingeschlossen, also z.B.: <P class=test>

Nun gibt es ja zum Glück reguläre Ausdrücke, dachte ich mir und wollte daher im ersten Schritt die Elementnamen wieder in kleingeschriebene Strings verwandeln:
ieValue = ieValue.replace(/<([A-Z]+)([^>]*?)>/ig, "XxX$1yYy".toLowerCase());
versucht.

Dieser Ausdruck liefert als Ergebnis zum Beispiel: xxxPyyy bei <P class=test>.
D.h. das toLowerCase() an sich greift - nur wird eben der Match $1 nicht konvertiert.

Lange Rede, kurzer Sinn: kennt jemand von Euch dieses Verhalten? Ist das IE-spezifisch? Oder ein neuer Bug? Der Dölling-Bug? Noch besser wäre es, wenn Ihr eine Lösung für mich hättet oder Lösungsvorschläge!

Thanx und ciao,
Andreas

  1. hi,

    ieValue = ieValue.replace(/<([A-Z]+)([^>]*?)>/ig, "$1".toLowerCase());
    Der Ausdruck ist nicht sonderlich komplex und matcht auch.
    Allerdings weigert sich der IE, die gefundenen Elementnamen durch die kleingeschriebene Version ("$1".toLowerCase()) zu ersetzen.

    ich würde mal vermuten, dass hier die zwei dinge nicht in der reihenfolge geschehen, die du gerne hättest.

    "$1" wird vermutlich in lowercase umgewandelt, bevor überhaupt replaced wird - und die "kleinschreibweise" von $1 ist ... nun ja, $1.

    innerhalb von replace() auf diese weise weitere JS-funktionen einzubinden, ist m.E. wenig sinnvoll, und "funktioniert" nicht wie gewünscht.

    Testweise habe ich es einmal mit dem Ausdruck:
    ...
    versucht.

    ja, hier das ganze von oben drüber nochmal zwischen zu kopieren, macht ein posting um vieles lesbarer :-)

    gruß,
    wahsaga

    --
    "Look, that's why there's rules, understand? So that you _think_ before you break 'em."
    1. ja, hier das ganze von oben drüber nochmal zwischen zu kopieren, macht ein posting um vieles lesbarer :-)

      Sorry, hätte die Nachricht vor dem Posten noch einmal lesen sollen.
      Mea culpa.
      Danke erst einmal für Eure Antworten.

      Im etwas verunglückten Teil meines Postings sollte dieser Ausdruck stehen:
      ieValue = ieValue.replace(/<([A-Z]+)([^>]*?)>/ig, "yYy$1XXx".toLowerCase());

      Dieser liefert mit etwa bei dem Quellstring <P class=test>:
      yyyPxxx

      D.h. die großen X und Y werden durch Kleinbuchstaben ersetzt. Das ist eben das, was mich erstaunt.
      Aber Ihr habt recht: es hängt mit der Reihenfolge der Verarbeitung zusammen. Eine elegante Lösung für dieses Problem fällt mir leider im Moment nicht ein.
      Bleibt eine Menge Wut auf den IE, der sich einmal mehr nicht standardkonform und nicht korrekt verhält (siehe "mutiertes" innerHTML)...

      Thanx und ciao,
      Andreas

      1. Hi,

        Aber Ihr habt recht: es hängt mit der Reihenfolge der Verarbeitung zusammen. Eine elegante Lösung für dieses Problem fällt mir leider im Moment nicht ein.

        Ändere die Reihenfolge. Wende toLowerCase auf das Ergebnis der Ersetzung an.

        cu,
        Andreas

        --
        Warum nennt sich Andreas hier MudGuard?
        Fachfragen per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
        1. Aber Ihr habt recht: es hängt mit der Reihenfolge der Verarbeitung zusammen. Eine elegante Lösung für dieses Problem fällt mir leider im Moment nicht ein.

          Ändere die Reihenfolge. Wende toLowerCase auf das Ergebnis der Ersetzung an.

          Und wie geht das?

          1. Hi,

            Aber Ihr habt recht: es hängt mit der Reihenfolge der Verarbeitung zusammen. Eine elegante Lösung für dieses Problem fällt mir leider im Moment nicht ein.
            Ändere die Reihenfolge. Wende toLowerCase auf das Ergebnis der Ersetzung an.
            Und wie geht das?

            bla=replace(...).toLowerCase()

            cu,
            Andreas

            --
            Warum nennt sich Andreas hier MudGuard?
            Fachfragen per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
            1. hi,

              Ändere die Reihenfolge. Wende toLowerCase auf das Ergebnis der Ersetzung an.
              Und wie geht das?

              bla=replace(...).toLowerCase()

              das haut dann doch aber wiederum alles klein, auch stringbestandteile, auf die der regex gar nicht matcht, oder?

              gruß,
              wahsaga

              --
              "Look, that's why there's rules, understand? So that you _think_ before you break 'em."
              1. Hi,

                Ändere die Reihenfolge. Wende toLowerCase auf das Ergebnis der Ersetzung an.
                Und wie geht das?
                bla=replace(...).toLowerCase()
                das haut dann doch aber wiederum alles klein, auch stringbestandteile, auf die der regex gar nicht matcht, oder?

                Ja und, was stört ausgerechnet Dich daran? Du hast doch sowieso was gegen Großbuchstaben ... ;-)

                Abgesehen davon: ein bißchen Schwund ist immer ;-)

                cu,
                Andreas

                --
                Warum nennt sich Andreas hier MudGuard?
                Fachfragen per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
                1. hi,

                  Ja und, was stört ausgerechnet Dich daran? Du hast doch sowieso was gegen Großbuchstaben ... ;-)

                  ich gebe mir trotzdem mühe, ihre existenz zu tolerieren.

                  gruß,
                  wahsaga

                  --
                  "Look, that's why there's rules, understand? So that you _think_ before you break 'em."
                2. Ja und, was stört ausgerechnet Dich daran? Du hast doch sowieso was gegen Großbuchstaben ... ;-)

                  Abgesehen davon: ein bißchen Schwund ist immer ;-)

                  Nope - wie oben schon erwähnt: es gibt ja auch Quellstrings dieses Formats:
                  <P class=formWrap> oder <DFN title=Dies ist eine Definition>
                  Und die will ich überführen in die korrekte Form:
                  <p class="formWrap"> bzw. <dfn title="Dies ist eine Definition">

                  Mit einem pauschalen toLowerCase() kommt man da nicht weiter.

                  Aber ich schau mal. Ggf. muß ich für den besch... IE eben eine sehr umständliche Version basteln (für Mozilla/Firefox läuft das ganze Tool schon längst....).

                  Thanx und ciao,
                  Andreas

        2. Ändere die Reihenfolge. Wende toLowerCase auf das Ergebnis der Ersetzung an.

          Das kann ich nicht machen, da ich im Quellstring zum Beispiel so etwas habe:
          <div class="formWrap">
          oder
          <dfn title="Dies ist eine Definition">

          Das macht die Sache ja so schwierig.
          ;)

          Ciao,
          Andreas

  2. Hi,

    ieValue = ieValue.replace(/<([A-Z]+)([^>]*?)>/ig, "$1".toLowerCase());
    Allerdings weigert sich der IE, die gefundenen Elementnamen durch die kleingeschriebene Version ("$1".toLowerCase()) zu ersetzen.

    Überlege, wann das toLowerCase auf "$1" angewandt wird - das geschieht bereits, bevor replace aufgerufen wird. "$1" wird in Kleinbuchstaben gewandelt (mit dem Ergebnis "$1") und dann an replace übergeben.

    cu,
    Andreas

    --
    Warum nennt sich Andreas hier MudGuard?
    Fachfragen per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
  3. Hallo,

    Nun gibt es ja zum Glück reguläre Ausdrücke, dachte ich mir und wollte daher im ersten Schritt die Elementnamen wieder in kleingeschriebene Strings verwandeln:
    ieValue = ieValue.replace(/<([A-Z]+)([^>]*?)>/ig, "$1".toLowerCase());

    Auf die Schnelle, als über leftContext, lastMatch und rightContext zu gehen:

    var str = "<A></A>...<A bla='a'>...</A>";
    var expr = /</?([A-Z]+)([^>]*?)>/ig;
    var erg;
    while (erg = expr.exec(str)) {
      alert(RegExp.leftContext + '[' + RegExp.lastMatch + ']' + RegExp.rightContext + ' (' + expr.lastIndex + ')\n' +
      "Treffer: [" + RegExp.$1 + "] und [" + RegExp.$2 + "]");
      str = RegExp.leftContext + RegExp.lastMatch.toLowerCase() + RegExp.rightContext;
    }
    alert("Ergebnis: " + str);

    MSIE kennt diese Eigenschaften ab Version 5.5.

    Wahrscheinlich gibt es noch eine einfachere Lösung, ich suche später noch einmal. MSIE 5.0 kann z.B. lastIndex, vielleicht ließe sich der Rest daraus herleiten (leftContext als Teilstring bis zum lastIndex, lastMatch als RegExp.$1 durch eine Klammerung des gesamten regulären Ausdrucks, rightContext als Teilstring vom Ende von lastMatch bis zum Stringende).

    Mathias

    1. var str = "<A></A>...<A bla='a'>...</A>";
      var expr = /</?([A-Z]+)([^>]*?)>/ig;
      var erg;
      while (erg = expr.exec(str)) {
        alert(RegExp.leftContext + '[' + RegExp.lastMatch + ']' + RegExp.rightContext + ' (' + expr.lastIndex + ')\n' +
        "Treffer: [" + RegExp.$1 + "] und [" + RegExp.$2 + "]");
        str = RegExp.leftContext + RegExp.lastMatch.toLowerCase() + RegExp.rightContext;
      }
      alert("Ergebnis: " + str);

      Hallo,

      aufbauend auf Deinen Vorschlag habe ich folgende Lösung für den IE entwickelt, die die in diesem Thread genannten Schwierigkeiten löst:

      var ieValue = myContainerNode.innerHTML();
         var expr = /(.*?</?)([A-Z]+)([^>]*?>.*?)/g;
         var erg;
         while (erg = expr.exec(ieValue)) {
          ieValue = RegExp.leftContext + erg[1] + erg[2].toLowerCase() + erg[3] + RegExp.rightContext;
         }
         var expr = /([^>="]+)="?([^>\s"]+)"?/g;
         erg = new Array();
         while (erg = expr.exec(ieValue)) {
          ieValue = RegExp.leftContext + erg[1].toLowerCase() + '="' + erg[2] + '"' + RegExp.rightContext;
         }

      Es bleibt ein kleines Problem: sollte in einem Text-Node ein String der Form Foo=Bar vorkommen, so wird er gnadenlos durch foo="Bar" ersetzt.

      Das ist jetzt noch der einzige Schwachpunkt, den ich erkennen kann.
      Lösung? Im Moment keine Idee.

      Ciao,
      Andreas

      1. Hallo,

        Du kannst z.B. die Attribut-Ersetzung in der Tag-Ersetzungsroutine unterbringen. Das grundlegende Problem ist und bleibt, dass du einen richtigen Parser bräuchtest, damit das Markup präzise aufbereitet werden kann. Die Frage ist, ob regulären Ausdrücke in JavaScript ein effizientes und effektives Mittel dazu ist. Gibt es keine andere Möglichkeit, als mit innerHTML zu arbeiten und diesen String clientseitig aufzubereiten? Was liest du da mit innerHTML überhaupt aus? Jede andere Alternativmöglichkeit, die sich bietet, erscheint mir sinnvoller, als eine entsprechende Routine in JavaScript über reguläre Ausdrücke zu schreiben.
        Wie auch immer, hier ein prototypische Demonstration eines abgewandelten Konzepts, das Attribute rudimentär berücksichtigt:

        var reststring = "<a bla=a>...</a>...<a BLA=BLUB MURKS=murks FOO='BAR'>...", stringneu = "", left = "", tag = "";
        var restattribute = "", attributeneu = "";

        var expr1 = new RegExp("(</?)([A-Z]+)([^>]*?)>");
        var expr2 = new RegExp("([A-Z]+)=([^ "']+)");
        var erg1, erg2;

        while (erg1 = expr1.test(reststring)) {
         alert(
          stringneu + " | " + reststring + "\n" +
          RegExp.leftContext + "[" + RegExp.lastMatch + "]" + RegExp.rightContext + "\n" +
          "Treffer: [" + RegExp.$1 + "] [" + RegExp.$2 + "] [" + RegExp.$3 + "]"
         );
         left = RegExp.leftContext;
         reststring = RegExp.rightContext;
         tag = RegExp.$1 + RegExp.$2.toLowerCase();
         restattribute = RegExp.$3;
         if (restattribute != "") {
          attributeneu = "";
          while (erg2 = expr2.test(restattribute)) {
           alert(
            "attributverarbeitung\n" +
            attributeneu + " | " + restattribute + "\n" +
            RegExp.leftContext + '[' + RegExp.lastMatch + "]" + RegExp.rightContext + "\n" +
            "Treffer: [" + RegExp.$1 + "] [" + RegExp.$2 + "]"
           );
           attributeneu += RegExp.leftContext + " " + RegExp.$1.toLowerCase() + "="" + RegExp.$2 + '"';
           restattribute = RegExp.rightContext;
          }
          attributeneu += restattribute;
          tag += attributeneu;
         }
         tag += ">";
         stringneu += left + tag;
        }
        stringneu += reststring;
        alert("Ergebnis: " + stringneu);

        Das wird viel zu kompliziert und bleibt trotzdem unzuverlässig, ich kann dir nur davon abraten und empfehlen, einen anderen Lösungweg zu suchen.

        Mathias