Gunnar Bittersmann: Boostrap-JS-Bug?

Ich versucher, das Bootstrap-JS einzubinden. Aber egal, ob ich das von [offizielle Stelle] downloade oder vom CDN per

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

einbinde, ich krieg immer einen Fehler: TypeError: t is undefined (in der nicht-minifizierten Variante ist es TypeError: $$$1 is undefined).

Mach ich was falsch oder hat Bootstrap gerade einen Bug?

LLAP 🖖

-- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
  1. Hallo Gunnar,

    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

    Ist das alles, was du dort einbindest?

    einbinde, ich krieg immer einen Fehler: TypeError: t is undefined (in der nicht-minifizierten Variante ist es TypeError: $$$1 is undefined).

    Mach ich was falsch oder hat Bootstrap gerade einen Bug?

    Wenn das alles ist, was du da einbindest, machst du was falsch. Bootstrap benötigt jQuery und Popper.js, die vorher geladen werden müssen.

    LG,
    CK

    -- https://wwwtech.de/about
    1. @@Christian Kruse

      Wenn das alles ist, was du da einbindest, machst du was falsch. Bootstrap benötigt jQuery und Popper.js, die vorher geladen werden müssen.

      Danke, das hatte ich geflissentlich überlesen.

      Ach du … Reichen denn 200 Kilobyte nicht? Nein, es müssen nochmal 100 mehr sein.

      300 Kilobyte nur dafür, dass man das Markup mit präsentationsbezogenen Klassen vollmüllen kann‽

      Wer Bootcrap einsetzt oder andere in den Wahnsinn treibt, das einzusetzen, dem gehen wohl die Nutzer am Arsch vorbei.

      LLAP 🖖

      -- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
      1. Tach!

        300 Kilobyte nur dafür, dass man das Markup mit präsentationsbezogenen Klassen vollmüllen kann‽

        Das geht auch ohne das Javascript. Zumindest wenn du dich auf die Dinge beschränkst (beschränken kannst), die kein Javascript benötigen. Wenn das nicht geht, kann zumindest noch der Popper hinterfragt werden und im Falle des Nichtbenötigens einfach eine leeres Objekt mit Namen Popper nehmen.

        Wer Bootcrap einsetzt oder andere in den Wahnsinn treibt, das einzusetzen, dem gehen wohl die Nutzer am Arsch vorbei.

        Als ob es die Nutzer interessiert, ob da präsentationsbezogenes Markup in einer Webseite steckt oder nicht.

        dedlfix.

        1. @@dedlfix

          Als ob es die Nutzer interessiert, ob da präsentationsbezogenes Markup in einer Webseite steckt oder nicht.

          Das nicht. Aber ob eine Seite eine Sekunde oder zehn Sekunden zum Laden braucht, das sehr wohl.

          LLAP 🖖

          -- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
      2. Tach!

        Ach du … Reichen denn 200 Kilobyte nicht? Nein, es müssen nochmal 100 mehr sein.

        Knapp 24 sind's bei mir, sowie 27, 7 und 15.

        300 Kilobyte nur dafür, dass man das Markup mit präsentationsbezogenen Klassen vollmüllen kann‽

        Macht zusammen 73. Ich weiß ja nicht, warum du deinen Nutzern unkomprimiertes Zeug zumuten möchtest. Oder woher hast du diese Zahlen? Meine stammen aus den Entwicklertools des Browsers und geben die übertragene Menge der in der Dokumentation erwähnten vier Ressourcen an.

        dedlfix.

        1. @@dedlfix

          Ich weiß ja nicht, warum du deinen Nutzern unkomprimiertes Zeug zumuten möchtest.

          Der Kram will nicht nur übertragen werden, sondern auch im Speicher liegen und geparst werden. Da sind wohl die unkomprimierten Zahlen aussagekräftiger.

          LLAP 🖖

          -- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
          1. Tach!

            Ich weiß ja nicht, warum du deinen Nutzern unkomprimiertes Zeug zumuten möchtest.

            Der Kram will nicht nur übertragen werden, sondern auch im Speicher liegen und geparst werden. Da sind wohl die unkomprimierten Zahlen aussagekräftiger.

            Auch das Argument ist nicht wirklich totschlagend. Man nimmt ja nicht umsonst CDNs, so dass diese Dateien im günstigen Fall nicht nur für die eigene Präsenz zählen. Und wenn es nicht deine Dateien sind, die im Cache liegen, dann sind es Dateien anderer Webseitenaufrufe, die den Platz belegen, bis das Cache-Management sie aufräumt. Dass diese Dateien verdrängt werden ist auch kein Beinbruch, denn die werden ja nicht umsonst als entbehrlich betrachtet.

            Man könnte den Spieß auch rumdrehen, denn deine Individualitäten sind es, die den Platz unteilbar belegen.

            Und ja, auch meine Argumentation ist lückenhaft, denn das System ist zu komplex, als dass einfache Erklärungen ihm gerecht werden. Nicht in der Betrachtung aufgeführt habe ich zum Beispiel, dass diese Dateien nur im Idealfall geteilt werden, verschiedene Webseiten aber auch gern mal sich auf eine Version festnageln und dann verschiedene Versionen im Umlauf sind.

            dedlfix.

            1. @@dedlfix

              Ich weiß ja nicht, warum du deinen Nutzern unkomprimiertes Zeug zumuten möchtest.

              Der Kram will nicht nur übertragen werden, sondern auch im Speicher liegen und geparst werden. Da sind wohl die unkomprimierten Zahlen aussagekräftiger.

              Man nimmt ja nicht umsonst CDNs […]
              Und ja, auch meine Argumentation ist lückenhaft

              Zumal sie auf „der Kram will auch geparst werden“ überhaupt nicht eingeht. Die Ausführung von JavaScript kostet Zeit – je mehr, desto leistungsschwächer der Prozessor. Und ich denke hier nicht an Systeme, die Entwickler auf ihrem Schreibtisch zu stehen haben, sondern an Systeme, die Nutzer in ihren Händen halten.

              Und die Ausführung von JavaScript kostet Akkuladung. Drei Websites besucht, Akku alle. Danke für nichts, Bootstrap.

              LLAP 🖖

              -- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
              1. Tach!

                Und ja, auch meine Argumentation ist lückenhaft

                Zumal sie auf „der Kram will auch geparst werden“ überhaupt nicht eingeht.

                Ja, den Teil hab ich unwillentlich übersehen.

                Die Ausführung von JavaScript kostet Zeit – je mehr, desto leistungsschwächer der Prozessor.

                Stimmt, generell. Ist aber für mich auch nur ein Argument der Diskussion willen, weil das Thema Akkuverbrauch nicht nur davon abhängt, also auch wieder nur ein Teil eines komplexen Systems ist. Wieviel Akku wird denn wirklich davon verwendet, und wie ist das Verhältnis zu den eigentlichen Energiefressern, dem Bildschirm beispielsweise. Sparst du vielleicht deutlich mehr Akku, wenn du die Anwendung weiß auf schwarz gestaltest? Kennst du zum Energieverbrauch belastbare Untersuchungen?

                Und die Ausführung von JavaScript kostet Akkuladung. Drei Websites besucht, Akku alle. Danke für nichts, Bootstrap.

                Übertreibungen helfen da auch nicht weiter und schmälern für mich eher den Wert deiner Aussagen.

                dedlfix.

                1. @@dedlfix

                  Stimmt, generell. Ist aber für mich auch nur ein Argument der Diskussion willen, weil das Thema Akkuverbrauch nicht nur davon abhängt, also auch wieder nur ein Teil eines komplexen Systems ist.

                  Ja, es ist nur ein Tropfen. Aber das sagt jeder von seinem Zeugs. Und so trägt halt jeder seinen Tropfen bei – bis das Fass überläuft und keiner dran schuld ist.

                  LLAP 🖖

                  -- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
      3. @@Gunnar Bittersmann

        Ach du … Reichen denn 200 Kilobyte nicht? Nein, es müssen nochmal 100 mehr sein.

        300 Kilobyte nur dafür, dass man das Markup mit präsentationsbezogenen Klassen vollmüllen kann‽

        Sarah Drasner hat das grade vortrefflich bebildert:

        When you load a whole library for some code you could probably write yourself: Auto mit großem Wandspiegel als Rückspiegel

        Wer Bootcrap einsetzt oder andere in den Wahnsinn treibt, das einzusetzen, dem gehen wohl die Nutzer am Arsch vorbei.

        LLAP 🖖

        -- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
    2. @@Christian Kruse

      Bootstrap benötigt jQuery und Popper.js, die vorher geladen werden müssen.

      “Place the following <script>s near the end of your pages, right before the closing </body> tag, to enable them. jQuery must come first, then Popper.js, and then our JavaScript plugins.”

      Heißt das, man kann die Scripte nicht mit defer im head einbinden? Weil bei defer nicht sichergestellt wäre, in welcher Reihenfolge sie abgearbeitet werden?

      LLAP 🖖

      -- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
      1. Hallo Gunnar,

        Bootstrap benötigt jQuery und Popper.js, die vorher geladen werden müssen.

        “Place the following <script>s near the end of your pages, right before the closing </body> tag, to enable them. jQuery must come first, then Popper.js, and then our JavaScript plugins.”

        Heißt das, man kann die Scripte nicht mit defer im head einbinden?

        Offensichtlich. (Nein, man kann sie nicht mit defer einbinden)

        Weil bei defer nicht sichergestellt wäre, in welcher Reihenfolge sie abgearbeitet werden?

        Keine Ahnung. Stop ranting.

        LG,
        CK

        -- https://wwwtech.de/about
        1. Tach!

          Stop ranting.

          Finde ich auch. Es hilft ja nicht, wenn man die Aufgabe erledigen muss, dazu aber nur mit Ablehnung an die Sache rangeht, sich nicht mit ihr beschäftigt, und am Ende nur ein Ergebnis rauskommt, das noch schlimmer (zumindest aus deiner Sicht @Gunnar) als der eigentlichen Bootstrap-Einsatz ist.

          dedlfix.

      2. Hallo Gunnar

        Heißt das, man kann die Scripte nicht mit defer im head einbinden? Weil bei defer nicht sichergestellt wäre, in welcher Reihenfolge sie abgearbeitet werden?

        Bei Verwendung des defer−Attributes ist sichergestellt, in welcher Reihenfolge die Skripte abgearbeitet werden, nämlich in der Reihenfolge, in der sie im Dokument notiert sind. Das gilt zumindest für alle modernen Browser mit einer standardkonformen Implementierung von defer.

        Wenn ich mich recht erinnere, gab es auch ein paar proprietäre Implementierungen von defer in Internet Explorern, bei denen die Ausführungsreihenfolge tatsächlich nicht gewährleistet war, aber diese Browser sind heutzutage, von wenigen Ausnahmefällen abgesehen, wohl kaum noch relevant.

        Eine einheitliche Auszeichnung vorausgesetzt, werden eingebundene Skripte nur dann nicht zwingend in der Reihenfolge ausgeführt, in der sie im Dokument notiert wurden, wenn das async−Attribut gesetzt ist, unabhängig davon, ob es sich dabei um klassische Skripte oder um Modulskripte handelt.

        <script src="path/to/script.js"></script>

        Wird ein klassisches Skript ohne defer oder async im Dokument notiert, dann wird der HTML−Parser wie du weißt an der entsprechenden Stelle angehalten, bis das Skript geladen, geparst und ausgeführt wurde, — oder bis ein Timeout zuschlägt, bevor die Ressource geladen werden konnte. Man spricht aufgrund dessen von einem blockierenden Skript.

        Für klassische Skripte ohne defer und async sieht das Ablaufdiagramm also wiefolgt aus:

        |-----| HTML Parsing |=====| Script Loading |xxxxx| Script Parsing and Execution |---------------| |---------------| |==========| |xxxxxxxxxx|

        Dadurch dass gewöhnliche Skripte unmittelbar abgearbeitet werden ist natürlich garantiert, dass die Skripte in der Reihenfolge ausgeführt werden, in der sie im Dokument notiert sind. Darüber hinaus ist garantiert, dass die Ausführung erfolgt bevor das Dokument fertig geparst wurde, also vor dem Eintritt des Ereignisses DOMContentLoaded.

        <script src="path/to/script.js"></script> </body>

        Weil die Ausführung des HTML-Parsers für die Verarbeitung eines solchen Skripts unterbrochen wird, wird natürlich auch der Aufbau der Seite verzögert, weshalb es gute Praxis ist, solche blockierenden Skripte am Ende des body zu notieren. Dadurch wird sichergestellt, dass die Blockade erst erfolgt, wenn die Inhalte der Seite dem Benutzer zur Verfügung stehen.

        <script src="path/to/script.js"></script> <link rel="stylesheet" href="path/to/stylesheet.css"/> </head>

        Ein solches Skript im Kopf des Dokuments zu notieren, gegebenenfalls sogar vor eingebundenen Stylesheets, ist dementsprechend ein anti−pattern. Ob das Skript aus einer externen Datei geladen wird oder als Elementinhalt notiert ist, macht dabei natürlich kaum einen Unterschied.

        <script defer="defer" src="path/to/script.js"></script>

        Für klassische Skripte die über ein src−Attribut verfügen, die also aus einer separaten Datei geladen werden und nicht direkt im Dokument eingebettet sind, kann zudem das defer−Attribut notiert werden. Solche Skripte werden, die Unterstützung durch den Browser vorausgesetzt, parallel zum Parsen des HTML-Dokuments geladen, nicht jedoch ausgeführt.

        Die Ausführung von Skripten mit defer−Attribut wird also verschoben/zurückgestellt (deferred), und zwar solange, bis der HTML-Parser das vorliegende Dokument verarbeitet hat.

        Für Skripte mit gesetztem defer−Attribut sieht das Ablaufdiagramm demnach so aus:

        |-----| HTML Parsing |=====| Script Loading |xxxxx| Script Parsing and Execution |------------------------------------------| |==========| |xxxxxxxxxx|

        Wie an der nicht-unterbrochenen Abschnitt für den HTML−Parser leicht zu erkennen ist, handelt es sich bei Skripten mit defer−Attribut um nicht-blockierende Skripte.

        Der HTML-Parser verwaltet eine sogenannte „Liste der nach dem Parsing auszuführenden Skripte“. Wird beim Parsen des Dokuments ein Skript mit defer−Attribut gefunden, dann wird dieses Skript der Liste hinzugefügt. Am Ende werden diese Skripte dann eins nach dem anderen ausgeführt; die Reihenfolge im Dokument bleibt also − wie eingangs bereits gesagt − erhalten.

        Wie dem zweiten Link auf die Spec oben zu entnehmen ist, erfolgt die Abarbeitung der mit defer zurückgestellten Skripte in standardkonformen Browsern auch vor DOMContentLoaded. Es ist also auch in solchen Skripten sicher, den Einstiegspunkt des Programms von diesem Event abhängig zu machen.

        <script defer="defer" src="path/to/script.js"></script> </body>

        Da Skripte mit defer das Parsing des Dokuments wie gesehen nicht blockieren, ist es entsprechend unsinnig sie am Ende des body zu notieren. Dadurch ist nichts gewonnen.

        <script defer="defer" src="path/to/script.js"></script> </head>

        Stattdessen möchte man diese Skripte möglichst weit oben im Dokument notieren, damit der — parallel zum Parsing des Dokuments erfolgende — Ladevorgang so früh wie möglich begonnen werden kann.

        <script type="module" src="path/to/module.js"></script>

        Bei Modulskripten darf das defer−Attribut nicht gesetzt werden. Das ist dort auch nicht nötig, denn Module implementieren standardmäßig das für defer spezifizierte Verhalten, im Übrigen unabhängig davon, ob das src−Attribut vorhanden ist oder nicht.

        Dieses Verhalten kann allerdings auch bei Modulen mit dem async−Attribut überschrieben werden, unter der Voraussetzung, dass eine externe Ressource geladen wird.

        <script async="async" src="path/to/script.js"></script>

        Das async−Attribut sorgt für eine asynchrone Ausführung des Skripts oder Moduls. Das bedeutet, ein entsprechendes Skript oder Modul wird zwar wie bei defer parallel zum Parsing des Dokuments geladen, es blockiert den HTML−Parser in diesem Zeitraum also nicht, aber es wird ausgeführt, sobald es zur Verfügung steht, und nicht zu einem vorbestimmten Zeitpunkt.

        Die Ausführung kann also vor dem Ende der Verarbeitung des Dokuments erfolgen oder danach, je nach dem, wann die geladenen Daten zur Verfügung stehen. Dem zur Folge sind Skripte mit gesetztem async-Attribut also potentiell blockierend.

        Ein Ablaufdiagramm für Skripte/Module mit gesetztem async−Attribut kann so aussehen:

        |-----| HTML Parsing |=====| Script Loading |xxxxx| Script Parsing and Execution |--------------------------| |---------------| |==========| |xxxxxxxxxx|

        Es ist grundsätzlich nicht vorherzusagen, wann ein asynchrones Skript ausgeführt wird, weshalb es auch nicht ratsam ist, die Ausführung des Programms von DOMContentLoaded abhängig zu machen.

        Wenn der Scriptcode mit dem DOM der Seite interagieren soll, sollte vor der Registrierung eines entsprechenden Eventhandlers zunächst die Eigenschaft readyState des Dokumentobjektes konsultiert werden:

        if (document.readyState !== 'loading') { doSomething(); } else { window.addEventListener('DOMContentLoaded', function (event) { doSomething(); }); }

        Ist das Parsing beendet, wird das Programm ausgeführt, sonst wird ein Eventhandler registriert um darauf zu warten, dass der HTML-Parser fertig ist.

        <script async="async" src="framework.js"></script> <script async="async" src="scriptThatDependsOnFramework.js"></script> </head>

        Das Attribut async ist auch in Situationen wie hier im Thread regelmäßig nicht das Mittel der Wahl, bei denen ein Skript von der vorherigen Ausführung eines anderen Skriptes abhängig ist …

        Ungeachtet von der Reihenfolge im Dokument wird das eine Skript aus dem Cache geladen und mehr oder weniger sofort ausgeführt, während ein anderes Skript über ein langsames Netzwerk geladen wird und entsprechend ewig auf sich warten lässt. Auf die Reihenfolge kann man sich hier nicht verlassen.

        Nochmal eine kleine Übersicht:

        Code Laden Ausführen Reihenfolge DOMContentLoaded
        <script> blockiert blockiert Dokument Ja
        <script async> parallel kann blockieren irgendeine nicht sicher
        <script defer> parallel nach dem Parsen Dokument Ja

        Viele Grüße,

        Orlok

        1. Hallo Orlok,

          +1

          vielen Dank für die ausführliche Erklärung. Sag mal noch die passende Stelle im Wiki.

          Bis demnächst
          Matthias

          -- Rosen sind rot.
        2. @@Orlok

          Vielen Dank für deine ausführliche Erklärung.

          Das heißt also, um bessere performance zu erzielen, ist es ratsam, die Scripte (jQuery, Popper, Bootstrap) nicht wie es bei Bootstrap geschrieben steht am Ende des body einzubinden, sondern im head mit defer.

          Wenn da die alten IE < 10 nicht wären, die buggy sind und eben die Reihenfolge der Abarbeitung nicht garantieren.

          LLAP 🖖

          -- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
    3. @@Christian Kruse

      Mach ich was falsch oder hat Bootstrap gerade einen Bug?

      Wenn das alles ist, was du da einbindest, machst du was falsch. Bootstrap benötigt jQuery und Popper.js, die vorher geladen werden müssen.

      Also doch ein Fehler von Bootstrap. Die Meldung TypeError: t is undefined ist Bullshit. Eine brauchbare Fehlermeldung wäre sowas wie Du Trottel hast jQuery und Popper nicht eingebunden.

      LLAP 🖖

      -- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
      1. Tach!

        Also doch ein Fehler von Bootstrap. Die Meldung TypeError: t is undefined ist Bullshit. Eine brauchbare Fehlermeldung wäre sowas wie Du Trottel hast jQuery und Popper nicht eingebunden.

        https://forum.selfhtml.org/self/2018/feb/6/boostrap-js-bug/1713316#m1713316

        dedlfix.

  2. Tach!

    Mach ich was falsch oder hat Bootstrap gerade einen Bug?

    Sowohl als auch.

    Die Datei hat zwar eine IIFE, ...

    (function ($$$1) { if (typeof $$$1 === 'undefined') { throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.'); } // ... })($);

    ... die eigentlich Bescheid sagen sollte, dass ... siehe Fehlermeldung. Aber da stehen noch andere Funktionen davor, die in ähnlicher Weise definiert wurden und auch aufgerufen werden. Aber die erste davon bekommt statt $ auch nur undefined übergeben und wirft dann einen Fehler, wenn auf Eigenschaften darauf zugegriffen werden sollen. An der Stelle ist natürlich Schluss und der Hinweis zur Behebung kann nicht mehr ausgeführt werden.

    dedlfix.

    P.S. Ist die Hölle zugefroren?

    Folgende Nachrichten verweisen auf diesen Beitrag:

    1. Hallo dedlfix,

      P.S. Ist die Hölle zugefroren?

      da muss mehr passiert sein.

      Gruß
      Jürgen

    2. @@dedlfix

      P.S. Ist die Hölle zugefroren?

      Als ob ich den Scheiß freiwillig machen würde.

      LLAP 🖖

      -- “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory