phoenix_live_view icon indicating copy to clipboard operation
phoenix_live_view copied to clipboard

Duplicate DOM nodes when using `phx-remove` with `JS.hide` and transition.

Open benvp opened this issue 3 years ago • 2 comments

Environment

  • Elixir version (elixir -v): 1.13.4
  • Phoenix version (mix deps): 1.6.11
  • Phoenix LiveView version (mix deps): 0.17.11
  • Operating system: macOS
  • Browsers you attempted to reproduce this bug on (the more the merrier): Chrome
  • Does the problem persist after removing "assets/node_modules" and trying again? Yes/no: Yes

Actual behavior

The setup is:

div element with a dynamic id, and a phx-remove with a JS.hide call. In addition there is a button which has a phx-click="next" which changes the id.

This would cause the old element to be replaced with a new element. However, if you add a transition and click the button during the transition, then additional DOM nodes will be created.

Waiting until the transition finishes and clicking the button again, restores the DOM to it's proper state.

def handle_event("next", _params, socket) do
  {:noreply, assign(socket, index: socket.assigns.index + 1)}
end

def render(assigns) do
  ~H"""
     <div
        id={"test-#{@index}"}
        class="w-24 h-24 bg-indigo-800 rounded-lg flex justify-center items-center"
        phx-remove={Phoenix.LiveView.JS.hide(transition: "some-transition-class", time: 2000)}
      >
        <span class="text-xl"><%= @index %></span>
      </div>

     <button type="button" class="px-4 py-2 bg-slate-800 rounded" phx-click="next">
       Next
     </button>
  """
end

Here a short gif demonstrating the behaviour. Check the DOM on the right:

CleanShot 2022-07-27 at 14 45 11

Expected behavior

I would expect the DOM to only contain the current visible element and not the elements, which have been hidden by JS.hide.

benvp avatar Jul 27 '22 12:07 benvp

does liveSocket.enableDebug() on the js console report any issues when this happens?

chrismccord avatar Jul 27 '22 13:07 chrismccord

Not sure if it helps, but that's the output from the debug after clicking 3 times while it's transitioning. Looks good to me, as the diffs update only the two numeric values in the id attribute and in the node content.

// phx-FwX0rxZgHtuy2AAm mount:  -
{
    "0": "<a class=\"font-bold font-title\" data-phx-link=\"redirect\" data-phx-link-state=\"push\" href=\"/\">benvp</a>",
    "1": "",
    "2": "",
    "3": {
        "0": "0",
        "1": " phx-remove=\"[[&quot;hide&quot;,{&quot;time&quot;:2000,&quot;to&quot;:null,&quot;transition&quot;:[[&quot;some-transition-class&quot;],[],[]]}]]\"",
        "2": "0",
        "s": [
            "<div class=\"mt-12 max-w-screen-md m-auto flex justify-center space-y-4 flex-col items-center\">\n  <div class=\"my-4 flex flex-wrap items-center justify-center space-x-4\">\n    <div id=\"test-",
            "\" class=\"w-24 h-24 bg-indigo-800 rounded-lg flex justify-center items-center\"",
            ">\n      <span class=\"text-xl\">",
            "</span>\n    </div>\n  </div>\n  <button type=\"button\" class=\"px-4 py-2 bg-slate-800 rounded\" phx-click=\"next\">\n    Next\n  </button>\n</div>"
        ]
    },
    "s": [
        "<header class=\"max-w-screen-md mx-auto px-8 lg:px-0\">\n  <nav class=\"py-4 flex justify-between\">\n    <div>\n",
        "\n    </div>\n\n    <div class=\"space-x-6\">\n",
        "\n",
        "\n    </div>\n  </nav>\n\n  <div class=\"-mx-[3%] w-[106%] h-[1px] bg-gradient-to-r from-transparent via-gray-500\"></div>\n</header>\n\n<main class=\"min-h-screen\">\n",
        "\n</main>\n\n<footer class=\"max-w-screen-md mx-auto mt-20 pb-4 px-8 sm:px-0\">\n  <div class=\"-mx-[3%] w-[106%] h-[1px] bg-gradient-to-r from-transparent via-gray-500\"></div>\n\n  <div class=\"py-4 flex flex-col sm:flex-row justify-between items-center text-gray-400 text-xs\">\n    <div class=\"flex items-center space-x-4\">\n      <a class=\"w-[20px] font-bold font-title hover:text-white\" href=\"mailto:[email protected]?subject=Hi Ben\">\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z\"></path><path d=\"M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z\"></path></svg>\n      </a>\n      <a class=\"w-[20px] font-bold font-title hover:text-white\" href=\"https://github.com/benvp\" target=\"_blank\" rel=\"noreferrer\">\n        <svg focusable=\"false\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 1.27a11 11 0 00-3.48 21.46c.55.09.73-.28.73-.55v-1.84c-3.03.64-3.67-1.46-3.67-1.46-.55-1.29-1.28-1.65-1.28-1.65-.92-.65.1-.65.1-.65 1.1 0 1.73 1.1 1.73 1.1.92 1.65 2.57 1.2 3.21.92a2 2 0 01.64-1.47c-2.47-.27-5.04-1.19-5.04-5.5 0-1.1.46-2.1 1.2-2.84a3.76 3.76 0 010-2.93s.91-.28 3.11 1.1c1.8-.49 3.7-.49 5.5 0 2.1-1.38 3.02-1.1 3.02-1.1a3.76 3.76 0 010 2.93c.83.74 1.2 1.74 1.2 2.94 0 4.21-2.57 5.13-5.04 5.4.45.37.82.92.82 2.02v3.03c0 .27.1.64.73.55A11 11 0 0012 1.27\"></path></svg>\n      </a>\n      <a class=\"w-[20px] font-bold font-title hover:text-white\" href=\"https://twitter.com/benvp_\" target=\"_blank\" rel=\"noreferrer\">\n        <svg focusable=\"false\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M22.46 6c-.77.35-1.6.58-2.46.69.88-.53 1.56-1.37 1.88-2.38-.83.5-1.75.85-2.72 1.05C18.37 4.5 17.26 4 16 4c-2.35 0-4.27 1.92-4.27 4.29 0 .34.04.67.11.98C8.28 9.09 5.11 7.38 3 4.79c-.37.63-.58 1.37-.58 2.15 0 1.49.75 2.81 1.91 3.56-.71 0-1.37-.2-1.95-.5v.03c0 2.08 1.48 3.82 3.44 4.21a4.22 4.22 0 0 1-1.93.07 4.28 4.28 0 0 0 4 2.98 8.521 8.521 0 0 1-5.33 1.84c-.34 0-.68-.02-1.02-.06C3.44 20.29 5.7 21 8.12 21 16 21 20.33 14.46 20.33 8.79c0-.19 0-.37-.01-.56.84-.6 1.56-1.36 2.14-2.23z\"></path></svg>\n      </a>\n    </div>\n\n    <p class=\"text-xs mt-2 sm:mt-0\">© 2022 Benjamin von Polheim • All rights reserved</p>\n  </div>\n\n</footer>"
    ]
}

// phx-FwX0rxZgHtuy2AAm update
{
    "3": {
        "0": "1",
        "2": "1"
    }
}

// phx-FwX0rxZgHtuy2AAm update:  - 
{
    "3": {
        "0": "2",
        "2": "2"
    }
}

// phx-FwX0rxZgHtuy2AAm update:  -
{
    "3": {
        "0": "3",
        "2": "3"
    }
}

And this is the HTML in the DOM. Somehow I end up three DOM nodes with all the differend ids test-1, test-2 and test-3

<div class="my-4 flex flex-wrap items-center justify-center space-x-4">
    <div id="test-1" class="w-24 h-24 bg-indigo-800 rounded-lg flex justify-center items-center" phx-remove="[[&quot;hide&quot;,{&quot;time&quot;:2000,&quot;to&quot;:null,&quot;transition&quot;:[[&quot;some-transition-class&quot;],[],[]]}]]" style="display: none;">
      <span class="text-xl">1</span>
    </div><div id="test-2" class="w-24 h-24 bg-indigo-800 rounded-lg flex justify-center items-center" phx-remove="[[&quot;hide&quot;,{&quot;time&quot;:2000,&quot;to&quot;:null,&quot;transition&quot;:[[&quot;some-transition-class&quot;],[],[]]}]]" style="display: none;">
      <span class="text-xl">2</span>
    </div><div id="test-3" class="w-24 h-24 bg-indigo-800 rounded-lg flex justify-center items-center" phx-remove="[[&quot;hide&quot;,{&quot;time&quot;:2000,&quot;to&quot;:null,&quot;transition&quot;:[[&quot;some-transition-class&quot;],[],[]]}]]">
      <span class="text-xl">3</span>
    </div>
</div>

benvp avatar Jul 28 '22 09:07 benvp

I tried to reproduce your issue and found that this was actually fixed in 0.18.2, specifically by 1abc20ca63331df984471c9b8a4fbf4a6cae0469 that introduces streams and also changed the handling of pending transitions.

If you still have issues, please send us a minimal reproduction (https://github.com/wojtekmach/mix_install_examples/blob/main/phoenix_live_view.exs can be used as basis) and we'll be happy to have another look. Thank you!

SteffenDE avatar Jan 26 '24 16:01 SteffenDE

That's good news. Will dig into it again as soon as I find some time. 👍

benvp avatar Jan 27 '24 21:01 benvp