Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LiveView interfering with custom js #81

jamilabreu opened this issue Mar 17, 2019 · 4 comments


None yet
5 participants
Copy link

commented Mar 17, 2019

Expected behavior

Any custom javascript should execute either after LiveView loads, or maybe have an observable event similar to Turbolinks:

document.addEventListener("turbolinks:load", function() {
  // ...

Actual behavior

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;

      if (element.clientHeight < element.scrollHeight) {
        // Do something that modifies the DOM

    // THIS DOES
    setTimeout(() => {
      if (element.clientHeight < element.scrollHeight) {
        // Do same thing
    }, 200);

This comment has been minimized.

Copy link

commented Mar 18, 2019

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!


This comment has been minimized.

Copy link

commented May 15, 2019

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?


This comment has been minimized.

Copy link

commented Jun 2, 2019

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()

$(document).on('phx:update', event => initSelectize())

This comment has been minimized.

Copy link

commented Jun 12, 2019

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}

def render(assigns) do
<label><%= @counter %></label>
<select class="select2">...</select>

def handle_info(:incr, socket) do
  Process.send_after(self(), :incr, 250)
  {:noreply, assign(socket, counter: socket.assigns.counter + 1) }


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()) {
    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 !?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.