How to use JS.add_class and JS.remove_class to change div color on phx-click

I am having difficulty getting a div (which is a menu option) to change colors after being clicked. I am admittedly not good with javascript. Ok … I’m actually quite terrible at it.

Here is the div that I’m trying to change when it is clicked (simplified for post):
I want it to be bg-gray-100 when static and bg-red-500 when clicked.

<div id={@id} 
       class="bg-gray-100" 
       phx-click=toggle_button_color("bg-red-500", "##{@id}")>

First I tried:

def toggle_button_color(js \\ %JS{}, new_color, to) do
  js
    |> JS.add_class(new_color, to: to)
    |> JS.remove_class(new_color, to: to)
  end

That didn’t work because of course the remove_class simply cancelled out the add_class.

Then I tried:

<div id={@id} 
       class="bg-red-500 !bg-gray-100" 
       phx-click=toggle_button_color("##{@id}")>

def toggle_button_color(js \\ %JS{}, to) do
    js
    |> JS.remove_class("!bg-gray-100", to: "{to}.!bg-gray-100")
    |> JS.add_class("!bg-gray-100", to: "{to}:not(!bg-gray-100")
  end

That didn’t work. I had to add the exclamation point because it kept showing it as red all the time.

I also tried:

JS.toggle(to: "##{@id}", in: "bg-red-500", out: "bg-gray-100")

Also didn’t work.

I’m sure there is an easy way to do this. Could someone point me in that direction? I tried ChatGPT but it keeps recommending a handle_event. I’m trying to avoid a trip to the server for something this simple and it seems like I should be able to do it with JS.add_class and JS.remove_class but I just can’t figure out how to add conditions to the “adding and removing.”

the next version of liveview will have JS.toggle_class see changelog: phoenix_live_view/CHANGELOG.md at main · phoenixframework/phoenix_live_view · GitHub

so wait a bit or run of main branch…

Another stop gap measure is to have two things, one adds the class, hides itself and shows the other. The other does the opposite.

1 Like

I got it to work using the following code. I thought I tried this above, but I must have made a mistake somewhere in that code. This works and will hold me until toggle_class comes out. Posting in case anyone else needs something similar.

 def toggle_menu_option(js \\ %JS{}, to) do
        js
        |> JS.remove_class("skl-selected-menu-option", to: "#{to}.skl-selected-menu-option")
        |> JS.add_class("skl-selected-menu-option", to: "#{to}:not(.skl-selected-menu-option)")
    end
4 Likes