Miserlou

Miserlou

Here’s one that should be easy, but I haven’t found summarized nicely.

I’ve got fairly standard LiveView application, but some of the elements need a bit of JavaScript for juicy interactive purposes (not standard controls, animations, etc.)

I’d like to have a button, which triggers a simple phx-click event, also fire some onclick javascript. However, these don’t work together (which I should have suspected). Is there a simple way of making these work together in tandem?

I’ve thought about just calling the JS event, then firing off of the phx-click from JS, but I don’t see a clear way of doing that without some server-side interaction, and I’ve just been using document.getElementById(my-phx-button").click() to send Phx event programatically from JavaScript so far.

Can anybody point me in the right direction?

Cheers!
Mis

Showing Posts 11 to 2

jdmarshall

jdmarshall

For posterity:

The problem you were encountering that none of the responders were clocking is that browsers are very opinionated about click events now. As part of preventing CSRF attacks, click events can only come from 1) a user action, 2) the debug console, or 3) fired from an onclick handler (eg, make a custom file upload button click the real button).

This happened about 10 years ago on some browsers and followed quickly to the rest.

And it’s why triggering it purely from the client side ended up working for you, and fiddling around in the console, but not a single one of the other suggestions did.

Miserlou

Miserlou OP

I’ve wrapped this and a bunch of other utilities for dealing with the JavaScript context from LiveView into a package here: GitHub - Miserlou/live_json: LiveJSON - LiveView for JSON · GitHub

Miserlou

Miserlou OP

Okay! Yes! This led me to a solution that works.

There are a few things that I think cause some confusion, both from the nomenclature and the lack of documentation.

First of all, be mindful of the difference between dispatchEvent(new Event("click", {bubbles: true})) and pushEvent('yourhandler', {foo: "bar"}. dispatchEvent is for in-DOM events, like button clicks, and pushEvent is for events that Phoenix will handle.

The second thing is that you will need to use a combination of both events to make this work.

So, here’s everything you need to submit a Phoenix event from interaction-free JavaScript.

First, define a hook in your app.js like so:

let Hooks = {}
Hooks.EventSender = {
    mounted() {
        this.el.addEventListener("eventSender", e => {
            this.pushEvent("your_phx_event", {foo: "bar"})
        })
    }
}
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks, params: {_csrf_token: csrfToken}})
liveSocket.connect()

Include that Hook on your template somewhere with a unique identifier:

<div id="sender_hook" phx-hook="EventSender"></div>

Then, to call it, send the DOM event in your JavaScript, which will be received by the hook, using the ID of the div you bound the hook to:

document.getElementById("sender_hook").dispatchEvent(new Event('eventSender'));

And this will send {foo: 'bar'} back to your your_phx_event handler on your server. Finally, note that if you are using this to have two ways of sending your phx events, one programmatic and one manual, you may need to use attributes or class membership as state to prevent recursion.

Thanks for the help, @malloryerik. I hope this helps anybody who finds it. (I also think the bit about using the Hook-bound object to send the event isn’t included in the documentation and probably should be.)

malloryerik

malloryerik

If I understand you, maybe in the hooks, like:

Hooks.HoverCatch = {
    mounted() {
        document.getElementById("read-space").addEventListener("mouseover", e => {
            if (e.target.id != "read-space") {
                this.pushEvent("mouseover", e.target.id)
            }
        })
    }
}

Or:

Hooks.SelectText = {
    mounted() {
        this.el.addEventListener("select", e => {
            start = this.el.selectionStart;
            ending = this.el.selectionEnd;
            this.pushEvent("select_text", {
                start,
                ending
            })
        })
    }
}

Kinda sorta? Pushing to the server from a hook?

Miserlou

Miserlou OP

Thanks, this is helpful, but I do already do this for pushing events from the server to the client without problem, the issue here is that I want to do the opposite.

I want to push an event to the server from client-side JavaScript, outside of user interaction with a bound element.

malloryerik

malloryerik

Maybe you’re looking for push_event? To call client JS from the server as in:

{:noreply, push_event(socket, "scores", %{points: 100, user: "josé"})}

You can then handle that on a LV hook or on a window.addEventListener(...) on your app.js.

The docs are pretty good if you get into them, at least that’s what I found. It took me a little bit to understand, so I wrote this up. Hopefully not adding to any confusion…

Maybe that helps? Have you also seen in the docs: handling server-pushed events and there’s also a section on custom events that you might want?

I’ve personally found that this area of LiveView has allowed me to make magical experiences with almost embarrassingly little effort.

Miserlou

Miserlou OP

A weird thing is that that does (sort of) work from the browser’s JS console, but it does not work when called from my application’s JS context.

Miserlou

Miserlou OP

Thanks, but that doesn’t work as expected. I want to send this event without interaction from an element or a hook.

cpgo

cpgo

Try this, I didnt test it, just basing on this reply Triggering LiveView Form Change Event from Javascript - #2 by chrismccord

document.getElementById("my-phx-button").dispatchEvent(new Event("click", {bubbles: true}))
Miserlou

Miserlou OP

Thanks Johnny! I had to upgrade my LiveView version, which made JS. commands available, and I can use a combination of JS.push and JS.dispatch to achieve my desired effect.

However, this appears to break my document.getElementById("my-phx-button").click() hack. Clicking on the button programmatically no longer sends the event. I need both, since my interface has two ways of triggering the event - a button click and a gesture. Now, clicking the button sends the event and plays the gesture animation, but the gesture no longer triggers the event.

I’ve looked quite thoroughly, but have not been able to find a clear way to send a synthetic event that isn’t tied to a hook or a form.

I see that my JS liveSocket object has a ‘dispatchEvent’ function, but this does not work:

liveSocket.dispatchEvent(new Event("submit_move", {foo: "bar"}));

What’s the secret sauce?

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews