Just playing around with Phoenix.LiveView.JS and wondering if there is a way to toggle classes on and off on an element? There is ‘add_class’ and ‘remove_class’ but ‘toggle’ looks like it is just used for showing and hiding. But maybe there is a way but just can’t see it.
2 Likes
Dabsy
January 28, 2022, 7:52pm
#2
I came here to ask this just now trying to make an indicator transform 180 degrees and not disappear.
phoenixframework:master
← nbw:feat/toggle-class
opened 03:03PM - 02 Nov 21 UTC
I've been implementing a live view app and came across an instance where I wante… d to toggle a class, not simply add or remove it.
I _could_ simply store the state in a live view component or on the html tag itself (to decide if I want to remove or add), but it seemed like maybe a toggle would be nice to have.
**I haven't added any function @doc comments-- yet**. If `toggle_classes` isn't the direction the live view team wants to go with, then feel free to close this PR. Otherwise, if it looks good then I'll also add the documentation too (something similar to add_classes or remove_classes) 👍
## Example
Here's a simple use case where toggle adds or removes a class "dark" which could be used for darkmode:
https://user-images.githubusercontent.com/3220620/139871078-3f4f5f6c-a53b-4ef8-944c-a5a8b291af70.mov
In order to get it to work I had to build phoenix live view assets locally and do some shenanigans with my mix.exs to use a local dependency, so I won't be sharing the app code, but here's the live view:
```elixir
defmodule PhxTesterWeb.TestLive do
use PhxTesterWeb, :live_view
alias Phoenix.LiveView.JS
def mount(_params, _, socket) do
{:ok, socket}
end
def toggle_mode(js \\ %JS{}) do
js
|> JS.toggle_class("dark", to: "body")
end
def test_button(assigns) do
~H"""
<div>
<button class="modal-button" phx-click={toggle_mode()}>toggle</button>
</div>
"""
end
end
```
and .heex file
```html
The button below will toggle dark mode.
<%= test_button(%{}) %>
```
## Code
I had issues getting jest to pass and left a comment in the code: https://github.com/phoenixframework/phoenix_live_view/pull/1721#discussion_r741164658
### Unrelated
Running mix format changes the code decently. Perhaps it's worth running it once or setting up format rules in a config file.
It looks like its not in master yet.
I’m using dispatch for now:
js = JS.dispatch(js, "icon:rotate-180", to: "#accordion")
window.addEventListener("icon:rotate-180", e => {
e.target.classList.toggle("transform-rotate-180")
});
4 Likes
Dabsy
February 7, 2022, 4:19pm
#3
I have an updated solution that supports more complexity inspired by some code I found in LiveBeats
# app.js
const execJS = (selector, attr) => {
# this executes rendered JS commands on an element
document.querySelectorAll(selector).forEach(el => liveSocket.execJS(el, el.getAttribute(attr)))
}
window.addEventListener('phx:collapse', e => {
const id = `#${e.target.id}`
if (e.target.getAttribute('data-open') === 'true') {
execJS(id, 'js-hide')
} else {
execJS(id, 'js-show')
}
})
~H"""
<div
id="my-accordian"
data-open="false"
phx-click={JS.dispatch("phx:collapse", to: "#my-accordian")}
js-show={show()}
js-hide={hide()}
>
...
</div>
"""
1 Like