Tom Schreiner: Transition bei Tausch von DOM children

Hallo,

ich habe folgendes Problem:

im Prinzip möchte ich zwei Bilder per Button mit einer Transition tauschen... ...also von

<div Parent_1><img Bild1></div>
<div Parent_2><img Bild2></div>

nach

<div Parent_1><img Bild2></div>
<div Parent_2><img Bild1></div>

Hatte da eine ziemlich aufwändige [wie auch sehr abenteuerliche] Variante am Start, mit Ermittlung der Koordinaten via getBoundingClientRect(), danach Verschieben via der entsprechenden -top, -right, -bottom, -left - Werte und ontransitionend eine Neubesetzung der img-src-Attribute mit den neuen Werten bei gleichzeitiger Entfernung der transition-Klasse und Wiederherstellung der Ursprungskoordinaten (was im Chrome manchmal ein kurzes Durchflackern der jeweiligen anderen Werte nach sich zog).

...gibt es da nicht eine elegantere Lösung?

dachte da an sowas in die Richtung wie replaceChild() bzw. removeChild() / appendChild() auf den parent-DIVs mit einer jeweiligen ID auf den IMGs (das erkennt das DOM aber nicht als "Verschieben" des IMGs an, daher bewirken CSS-transitions nichts).

Danke! Tom

  1. Lieber Tom,

    im Prinzip möchte ich zwei Bilder per Button mit einer Transition tauschen...

    in Sachen Bildwechsler haben wir da was.

    Liebe Grüße

    Felix Riesterer

    1. Hallo Felix,

      Hab ein bißchen reingelesen, gründlich und top erklärt!

      Nur leider gar nicht das, was ich suche.

      Wie eingangs bereits dargelegt habe ich zwei unterschiedlich platzierte DIVs mit jeweils einem IMG, per Click sollen die IMGs per transition, aber auch semantisch die Plätze tauschen.

      LG Tom

      1. Hallo Tom,

        ich habe mich gefragt, warum die Bilder zwingend ein neues Parent bekommen müssen, aber ich denke ich weiß die Antwort: responsive layout. Wenn die Parent-Beziehung nicht geändert wird, müsstest Du bei jeder Layoutänderung die Positionen neu berechnen. Das kann aufwändig werden. Die Wahrscheinlichkeit, dass sich das Layout während der Transition ändert, ist vernachlässigbar; und wenn doch, tough luck, dear user.

        Ich glaube, deine handberechnete Verschiebung musst Du beibehalten.

        Aber ein Ändern des src-Attributs solltest Du wirklich lassen. Das ist eine asynchrone Operation, weil der Browser das Bild erstmal laden muss - auch wenn's nur der Cache ist -, und dazwischen wird neu gezeichnet.

        function HandleSwapTransitionEnd() {
           let img1 = document.getElementById("bild_1");
           let img2 = document.getElementById("bild_2");
           let div2 = img2.parentNode;
        
           div2.appendChild(img1.replaceWith(img2));
           // transition entfernen
           // top/left von img1 und img2 entfernen
        }
        

        müsste eigentlich den Zweck erfüllen. replaceWith ersetzt ein DOM Element durch ein anderes und liefert das ersetzte Element zurück (hier also img1). D.h. diese eine Zeile am Schluss sollte effizient die Parent-Beziehungen vertauschen. Danach kannst Du die Styles geradeziehen.

        Hierbei passiert nichts asynchrones, d.h. der Browser sollte erst dann die Layout-Maschine neu anwerfen wenn das JavaScript durchgelaufen ist.

        Das Attribut ontransitionend solltest Du ebenfalls nicht nutzen. Sowas macht man heutzutage unaufdringlich mit

        let img1 = document.getElementById("img1")
        img1.addEventListener("transitionend", HandleSwapTransitionEnd);
        

        Das muss nur so platziert sein, dass es das Element bereits gibt wenn der Code läuft, d.h. entweder gehört das Script an's Seitenende oder Du musst die Registrierung in einen DOMContentLoaded Handler packen.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. (...) aber ich denke ich weiß die Antwort: responsive layout.

          BINGO! Voll ins Schwarze! ☺️

          Danke auch für die Idee mit appendChild (...) replaceWith (...), allerdings steht mir dann das ersetzte IMG ja nicht mehr zur Verfügung (wenn ich das richtig interpretiere).

          ...setze also ein appendChild(IMG1) und gleichzeitig ein appendChild(IMG2)auf ein anderes Zielelement.

          ...die Frage: Kopie oder Neuzuweisung?

          Produziere ich hier eh keine ungewollten Doppelgleisigkeiten, im Sinne von dass appendChild(IMG1) eine Kopie von IMG1 erstellt und das Element nicht (wie gewünscht) verschiebt? (Scheint im Inspektor so zu sein, dass IMG1 wie gewünscht verschoben, daher vom AusgangsDIV entfernt wird, darüber hinaus wirft removeChild() vor appendChild() den Fehler dass node kein Child ist).

          Zusammenfassend:

          Gehe ich recht in der Annahme, dass DIV2.appendChild(IMG1) das Bild aus DIV1 sozusagen automatisch entfernt/löscht?

          Danke und gesund bleiben, Tom.

          1. Hallo Tom,

            wenn man von diesem DOM ausgeht:

            <div id="div1"><img id="img1" src="..." alt="..."></div>
            <div id="div2"><img id="img2" src="..." alt="..."></div>
            

            dann macht diese Zeile

            div2.appendChild(img1.replaceWith(img2));
            

            folgendes:

            • zuerst mal der replaceWith Aufruf:

              • replaceWidth merkt sich img1
              • Dann wird img2 aus der Children-Liste von div2 gelöscht und an Stelle von img1 in die Children-Liste von div1 geschrieben
              • Die Parent-Beziehung von img1 wird gelöscht
              • Die Parent-Beziehung von img2 wird angepasst
              • img1 ist jetzt ein Waisenkind und hat Angst vor dem bösen Garbage Collector. replaceWidth schmeißt es achtlos auf den Stack und geht nach Hause.
            • appendChild kommt daher

              • appendChild findet ein trauriges img1 auf dem Stack und nimmt sich seiner an
              • img1 wird an die Children-Liste von div2 angehängt und die Parent-Beziehung von img1 wird neu gesetzt. Damit ist seine Waisenzeit vorbei und die beiden freuen sich über ihre neue Familie.
              • Der gruselige Garbage Collector kommt vorbei und knurrt „Ich sah hier ein elternloses DOM Element!“ Aber div2 legt sich schützend um das zitternde img1 und sagt nur: „Das ist mein Kind, das bleibt bei mir!“

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Mit anderen Worten: kann man auch irgendwie so zusammenfassen.