Search code examples
javascriptruby-on-railsturbolinksturbolinks-5bootstrap-native

Bootstrap Native does not work with Turbo Links


I am trying to get Bootstrap Native to work with Turbolinks 5 in a Rails 5 app. When I first load the page, the Bootstrap drop down menu works fine, but after navigating to another page, the Bootstrap drop down no longer works. It is as if Bootstrap's event listeners get disconnected.

I have seen several questions addressing this in issue with respect to Bootstrap's jQuery implementation, however, I am interested in using Bootstrap Native and eliminating jQuery from my JS stack.

Here are some specifics:

application.js

# app/assets/javascript/application.js
//= require turbolinks
//= require rails-ujs
//= require polyfill
//= require bootstrap-native

application layout

# views/layout/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  </head>
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <%= yield %>
    </div>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload', 'data-turbolinks-eval': 'false' %>
  </body>
</html>

bootstrap-native This is the Bootstrap Native package.

Removing 'data-turbolinks-eval': 'false' from my Javascript tag, thereby re-evaluating the all of the app's Javascript on every Turbolink navigation, does solve the Bootstrap Native problem, but it but it causes rails-ujs to throw an exception.

Any thoughts on how to solve this?


Solution

  • BSN developer here, thinking why not add this custom code outside the BSN library, for easier maintenance?

    var container = document.getElementById('myContainer');
    document.addEventListener('turbolinks:load', function(){
      container = container || document.getElementById('myContainer');
      Array.prototype.forEach.call(container.querySelectorAll('[data-spy="affix"]'), function(element){ new Affix(element) });
      Array.prototype.forEach.call(container.querySelectorAll('[data-dismiss="alert"]'), function(element){ new Alert(element) });
      Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="buttons"]'), function(element){ new Button(element) });
      Array.prototype.forEach.call(container.querySelectorAll('[data-ride="carousel"]'), function(element){ new Carousel(element) });
      Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="collapse"]'), function(element){ new Collapse(element) });
      Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="dropdown"]'), function(element){ new Dropdown(element) });
      Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="modal"]'), function(element){ new Modal(element) });
      Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="popover"]'), function(element){ new Popover(element) });
      Array.prototype.forEach.call(container.querySelectorAll('[data-spy="scroll"]'), function(element){ new ScrollSpy(element) });
      Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="tab"]'), function(element){ new Tab(element) });
      Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="tooltip"]'), function(element){ new Tooltip(element) });
    },false);
    

    You should only look into the specific container turbolinks update, as you don't update anything else and you need this to be as fast as possible.

    I also updated the wiki page to include an example for general purpose.

    UPDATE Starting with BSN version 2.0.20, you can use the library in your site <head> without any additional scripting required, and you can do this turbolinks easier:

    var container = document.getElementById('myContainer');
    document.addEventListener('turbolinks:load', function(){
      container = container || document.getElementById('myContainer');
      BSN.initCallback(container);
    }, false);
    

    If you type in BSN hit Enter in your console, you get the following object:

    BSN = {
      version: '2.0.20',
      initCallback: function(lookup){},
      supports: [ /* an array with supported components */ ]
    }
    

    Please remember that you don't have to use myContainer as the ID attribute for your turbolinks target, you can use anything to make that element unique, I would suggest a selector like data-function="turbolinks-autoload".

    Download latest master and give it a try.