Any custom javascript should execute either after LiveView loads, or maybe have an observable event similar to Turbolinks:
document.addEventListener("turbolinks:load", function() {
// ...
})
I'm seeing that LiveView often interferes with my custom js, but adding a delay fixes things. In my case using Stimulus:
import { Controller } from "stimulus";
export default class extends Controller {
static targets = ["element"];
connect() {
const element = this.elementTarget;
// THIS DOESN'T EXECUTE
if (element.clientHeight < element.scrollHeight) {
// Do something that modifies the DOM
}
// THIS DOES
setTimeout(() => {
if (element.clientHeight < element.scrollHeight) {
// Do same thing
}
}, 200);
}
}
Since we patch the Dom, your elements can appear/update/disapear at any time, so any event handlings on the DOM element in your JS aren't guaranteed to work. We dispatch a single phx:update event on the document today, but the javascript interop story is still TBD. We plan to tackle that once other areas are in better shape and we have enough usecases to make a generalized JS interop solution. Thanks!
OMG, phx:update is the event that I was looking for. In my case I need to re-initialize select2 every time Phoenix Live View changes the page.
Just sharing here, maybe it is going to help others:
const initSelect2 = () => {
$('.select2').select2({ theme: 'bootstrap' })
}
// Re-initialize the select2 plugin every time Phoenix Live View changes the DOM.
$(document).on('phx:update', event => initSelect2())
@chrismccord, should we document this in somewhere?
I'll post this here - I was in the same situation with selectize, here's what worked here:
const initSelectize = () => {
$('.selectize_typeahead').each(function() {
if (this.selectize) {
var value = $(this).val(); // store the current value of the select/input
$(this)[0].selectize.destroy(); // destroys selectize()
$(this).val(value);
}
});
$(".selectize_typeahead").selectize();
}
$(document).on('phx:update', event => initSelectize())
Did you have any problem reinitializing elements like select tag with select2?
Once select2 is initialized, it changes the select tag attributes. The problem I see re-initializing select/selectize at each update, is that if we have in the same LiveView template something updating many times per second (like a counter or a market price), each time the select tag is patched back to the original, and then calling the initialization many times per second we make the select dropdown almost impossible to use (and I don't even talk about performance)
I've simulated it sending messages to the LiveView process every 200ms
def mount(_session, socket) do
socket = assign(socket, counter: 0)
Process.send_after(self(), :incr, 200)
聽{:ok, socket}
end
def render(assigns) do
~L"""
<label><%= @counter %></label>
<select class="select2">...</select>
"""
end
def handle_info(:incr, socket) do
Process.send_after(self(), :incr, 250)
{:noreply, assign(socket, counter: socket.assigns.counter + 1) }
end

We can put the select in another LiveView, but this is not enough since (If I understood correctly) phx:update is a generic event triggered for any update handled by any LiveView in the page, so our select2 element will be reinitialized for every LV update event in the page.
The only way I've found to solve this is to put the select element in a separate LiveView and then on each single phx:update event checks if select2 has been already initialized - checking if a class is present.
function initSelect2(selector) {
if (!isInitialized()) {
$(selector).select2()
}
function isInitialized() {
return $(selector).hasClass("select2-hidden-accessible")
}
}
In this way only when we change the select it's patched and re-initialized.

It works but still... it's a workaround and maybe it would be better to use channels directly !?
Just wanted to drop a video I made about this recently: https://youtu.be/ziuflbasrgk
I think it can be helpful for those who want a walk through on how to do this. I did have an issue with reinitialize JS, and for those inputs I kept them just on initialize for document ready and also do some checking when initializing to see if they are already initialized or not.
Most helpful comment
Just wanted to drop a video I made about this recently: https://youtu.be/ziuflbasrgk
I think it can be helpful for those who want a walk through on how to do this. I did have an issue with reinitialize JS, and for those inputs I kept them just on initialize for document ready and also do some checking when initializing to see if they are already initialized or not.