Leander: Das zweite von tausend Elementen finden

Guten Abend,

Folgendes HTML:

<table id="lorem">
    <tr>
        <td>
            <p>foo</p>
        </td>
        <td>
            <p>bar</p>
        </td>
    </tr>
    <tr>
        <td>
            <p>baz</p>
        </td>
        <td>
            <p>quux</p>
        </td>
    </tr>
    <!-- 998 more <tr>s --->
</table>

Wie bekomme ich am schnellsten das zweite p (also <p>bar</p>)?

Der jQuery Weg wäre dieser:

$("#lorem p").eq(1);

Es findet also zuerst alle tausend p's und wirft dann die letzten 998 weg (richtig?). Geht das besser (mit JQuery oder Vanilla.js)?

Gruß, Leander

  1. @@Leander

    $("#lorem p").eq(1);
    
    

    Es findet also zuerst alle tausend p's und wirft dann die letzten 998 weg (richtig?). Geht das besser (mit JQuery

    Das p-Element in der letzten Zelle der ersten Zeile selektieren:

    $('#lorem tr:first-child > td:last-child > p')
    

    Ob das wirklich schneller ist, müsste man testen.

    oder Vanilla.js)?

    Genauso:

    document.querySelector('#lorem tr:first-child > td:last-child > p')
    

    Oder, da querySelector() das erste passende Element liefert, genügt auch

    document.querySelector('#lorem td:last-child > p')
    

    LLAP 🖖

    --
    Ist diese Antwort anstößig? Dann könnte sie nützlich sein.
    1. Hallo,

      Mein Beispiel war mißverständlich, es geht auch um Tabellen, wo ich die genaue Verteilung der <p>s nicht kenne:

      <table id="lorem">
          <tr>
              <td>
                  <p>foo</p>
                  <p>bar</p>
              </td>
              <td>
              </td>
          </tr>
          <tr>
              <td>
                  <p>baz</p>
              </td>
              <td>
                  <p>quux</p>
              </td>
          </tr>
          <!-- 998 more <tr>s --->
      </table>
      

      ich bräuchte also so etwas wie einen :nth-descendant-of-type-Selektor, aber den gibts (noch) nicht. Danke trotzdem für die Hilfe.

      Gruß Leander.

      1. @@Leander

        Mein Beispiel war mißverständlich,

        Ja. Kannst du nochmal eindeutig sagen, was du denn eigentlich willst? Das zweite p-Element innerhalb von #lorem, egal in welcher Tabellenzelle es sich befindet?

        LLAP 🖖

        --
        Ist diese Antwort anstößig? Dann könnte sie nützlich sein.
        1. Kannst du nochmal eindeutig sagen, was du denn eigentlich willst? Das zweite p-Element innerhalb von #lorem, egal in welcher Tabellenzelle es sich befindet?

          genau das meine ich.

          1. @@Leander

            Kannst du nochmal eindeutig sagen, was du denn eigentlich willst? Das zweite p-Element innerhalb von #lorem, egal in welcher Tabellenzelle es sich befindet?

            genau das meine ich.

            Du kannst also auch nicht sicher sein, ob das zweite p-Element in der ersten Tabellenzeile steht? Also die Suche beschränken auf document.querySelectorAll('#lorem tr:first-child p')[1] geht nicht?

            Sonst wärst du wieder bei dem, was du schon hattest: in Vanilla-JS document.querySelectorAll('#lorem p')[1].

            Was dann aber vielleicht performanter wäre:

            document.querySelector('#lorem p').classList.add('first');
            var second = document.querySelector('#lorem p:not(.first));
            

            LLAP 🖖

            --
            Ist diese Antwort anstößig? Dann könnte sie nützlich sein.
            1. Hallo,

              Was dann aber vielleicht performanter wäre:

              document.querySelector('#lorem p').classList.add('first');
              var second = document.querySelector('#lorem p:not(.first));
              

              Es geht mir generell um einen Iterator, der stoppt, sobald das gewünschte nte Element gefunden ist, und der nicht zuerst alle Elemente sucht und dann erst das nte Element herausfiltert. Ziemlich seltsam, daß es so einen Iterator für die Suche von DOM-Elementen in Javascript nicht zu geben scheint, in jeder anständigen Programmiersprache ist das selbstverständlich, Beispiel Python:

              # hier wird nicht zuerst eine Liste mit Hundert Millionen Elementen erzeugt,
              # sondern ein Iterator verwendet, weshalb das Programm sofort beendet ist.
              for x in range(100000000):
                  # zum Vergleich nachfolgende Zeilen mit einem 'pass'-Statement ersetzen
                  if x==5: 
                      break
              
              
              1. @@Leander

                document.querySelector('#lorem p').classList.add('first');
                var second = document.querySelector('#lorem p:not(.first));
                

                Es geht mir generell um einen Iterator, der stoppt, sobald das gewünschte nte Element gefunden ist, und der nicht zuerst alle Elemente sucht und dann erst das nte Element herausfiltert.

                Ja, schon klar.

                Bei obigem Code werden auch nicht alle Elemente durchsucht, sondern nach dem jeweils ersten gefundenen wird abgebrochen.

                Wenn du – wie du eingangs schriebst – das zweite Element suchst, ist das praktikabel. Allgemein fürs n-te Element eher nicht.

                LLAP 🖖

                --
                Ist diese Antwort anstößig? Dann könnte sie nützlich sein.
                1. Bitte vermeide es in Zukunft mit mir zu sprechen, Mr. Guck-auf-den-Finger.

                  1. @@Leander

                    Bitte vermeide es in Zukunft mit mir zu sprechen, Mr. Guck-auf-den-Finger.

                    1. Lass ich mir nicht den Mund verbieten.

                    2. Verstehe ich dein Verhalten nicht.

                    Ich habe dir eine Lösung für dein ursrünglich geschildertes Problem genannt. Dann musstest du zugeben, dass du dein Problem nicht richtig geschildert hattest.

                    Dann hab ich dir eine Lösung für dein nun geschildertes Problem genannt, bei der auch nicht alle Elemente durchsucht werden müssen. Was gefällt dir daran jetzt nicht? Dass die Lösung zwar fürs zweite (wie von dir verlangt), aber nicht fürs n-te Element taugt? Hast du erneut dein Problem nicht richtig geschildert?

                    Na gut, ich muss dein Verhalten ja auch nicht verstehen. Wenn du es so willst: *PLONK*

                    LLAP 🖖

                    --
                    Ist diese Antwort anstößig? Dann könnte sie nützlich sein.
              2. Moin,

                Es geht mir generell um einen Iterator, der stoppt, sobald das gewünschte nte Element gefunden ist, und der nicht zuerst alle Elemente sucht und dann erst das nte Element herausfiltert.

                Du musst bedenken, dass man auf die Implementierungsdetails von document.querySelectorAll() keinen Einfluss hat. Man weiß nicht welcher konkrete Algorithmus bei der Suche eingesetzt wird (Die Spezifikation gibt das nicht eindeutigst vor). Da das zurückgegebene NodeList Objekt die Menge der gefundenen Elemente kennt, darf man davon ausgehen, dass keine interne Optimierung durch bspw. den Einsatz von Generators stattfinden kann, was aber die Basis für eine inkrementelle Suche - und deinen Wunsch nach einem terminierenden Iterator - wäre.

                Ziemlich seltsam, daß es so einen Iterator für die Suche von DOM-Elementen in Javascript nicht zu geben scheint

                Mit den "Standardmitteln", ist dein Problem wohl nicht zu lösen. Da du aber ohnehin Anforderungen hast, die über das alltägliche hinausgehen, möchtest du dir vielleicht den TreeWalker genauer anschauen. Dieses Interface ermöglicht dir einen sehr tiefen aber dennoch komfortablen Eingriff in die DOM-Suche. Der TreeWalker enthält dabei nichts weiter als die Beschreibung wie gesucht werden soll und eine Iterator-Implementierung, die dir die absolute Kontrolle über den Suchvorgang gibt.

                Ob du jedoch mit einem TreeWalker tatsächlich besser (also schneller, performanter) fährst, als mit dem handelsüblichen querySelectorAll() bleibt noch zu messen. Dafür bietet sich jsperf an.

                …, in jeder anständigen Programmiersprache ist das selbstverständlich, Beispiel Python:

                # hier wird nicht zuerst eine Liste mit Hundert Millionen Elementen erzeugt,
                # sondern ein Iterator verwendet, weshalb das Programm sofort beendet ist.
                for x in range(100000000):
                    # zum Vergleich nachfolgende Zeilen mit einem 'pass'-Statement ersetzen
                    if x==5: 
                        break
                

                Nach einer kurzen Recherche stellt sich heraus, dass Python 2 eine Liste aller Werte generierte und zurück gab, erst in Python 3 wird der nächste Wert erst beim Abruf berechnet. Dieses Konzept nennt man gemeinhin "Generator". "Iterator" ist lediglich das definierte Interface zum Durchlaufen von Sequenzen, sagt aber nichts über die Erstellung der Sequenz aus. Generator kommen mit ES6 nach JavaScript - das wird aber auf querySelectorAll() keinen Einfluss haben.

                Grüße, Rod