Is there a way to temporarily JS.set_attribute?

Is there a way to get LiveView to add an attribute and then automatically remove it after a timeout?

I want to do something like this:

<button phx-click={JS.set_attribute({"data-foo", true}, remove_after: 1500)}>Click Me</button>

I don’t think you can acheive this with JS commands alone right now. In cases like these I usually just JS.dispatch("some-event" and implement the rest in JavaScript.

1 Like

I also have not seen a specific API for that.

Yes, as you said, I think our best bet now is JS.dispatch/2.

JS commands used to be always blocking, so that’s probably why the only supported API closest to temporarily setting an attribute is JS.transition (which was meant to stay within 200~300ms as per Chris), which temporarily sets classes.

Steffen added a new option blocking: false to all JS commands that can do transitions in LV 1.0, so perhaps there’s a way forward to introducing a new temporary attribute command now.

1 Like

Out of curiosity, what is the use-case for setting an attribute temporarily?

If it’s something that overlapped with CSS functionality then of course you could use transitions instead.

1 Like

A recent one I had was to set a data attribute temporarily. Could be done with a class, though.

The data attribute was used to render different states solely from CSS/JS on the client side. To be more specific, the particular case I had was to show a message like “Copied!” after clicking a button that copies content to clipboard (example).

In Tailwind, it becomes something like [data-copied]: vs [.copied]: for a custom selector.

2 Likes

FYI there is import {createHook} from “phoenix_liveview”` where you can drive programmatic js commands from client code, which could for example do a “temporary” set thing by removing the attr:

  this.hook = createHook(this.el, {})
  const js = this.hook.js();

  js.setAttribute(someElement, "foobar", "baz");
  //...
  js.removeAttribute(someElement, "foobar");

And things will be sticky just like HEEx JS commands.

6 Likes

Thanks for the idea.

Where is this example code supposed to be written, in app.js? Within another hook?

I believe Chris’ example is how to use it outside of a hook. Within a hook, you can do this.js().setAttribute (note the function call here). This is super recent—I learned it from another thread here—and I believe is still undocumented.

Can confirm. I’ve started to use it on my app Hooks and it works wonders but I had to dig through the code to understand how to use it.

This is exactly the kind of use-case I had in mind.

It’s pretty great! this.js().exec is particular helpful.

I minor gotcha I had: while all the other commands work essentially the same, exec takes the command itself as opposed to the attribute it lives in:

const cmd = this.el.getAttribute("[phx-click-away]")
this.js().exec(cmd)

Thanks, @LostKobrakai, for helping me out with that one.

While I’m sick of hearing the term “game changing,” this is pretty game changing since if we’ve constructed a JS command that stands on its own, then need something more complicated in a hook that also needs the same functionality as the command, we no longer need to re-implement it JS.

1 Like

Is the idea here to just create a <script> tag inside a component and place the code there?
If so, that could be interesting to make hooks live alongside the component instead of separated in another path.