Major Revision
In the wake of the accepted answer by a member of the Bootstrap Native development team, I have revised the solution I implemented in my app. My solution differs slightly from the accepted solution because my listener will search the entire DOM for Bootstrap components. The accepted solution, by contrast, will only search within an HTML tag that has id="myContainer
. The accepted solution will execute faster because it only searches a subset of the DOM. However, it requires the developer to wrap the relevant Bootstrap components in a tag with the myContainer
id.
Either solution works. My solution will run a little slower, but leads to easier coding and is less prone to developer induced bugs. Here are the details:
app/assets/javascript/application.js
//= require turbolinks
//= require rails-ujs
//= require polyfill
//= require bootstrap-native
//= require bootstrap-native-turbolinks
app/assets/javascript/bootstrap-native-turbolinks.js
document.addEventListener('turbolinks:load', function(){
Array.prototype.forEach.call(document.querySelectorAll('[data-spy="affix"]'), function(element){ new Affix(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-dismiss="alert"]'), function(element){ new Alert(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="buttons"]'), function(element){ new Button(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-ride="carousel"]'), function(element){ new Carousel(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="collapse"]'), function(element){ new Collapse(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="dropdown"]'), function(element){ new Dropdown(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="modal"]'), function(element){ new Modal(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="popover"]'), function(element){ new Popover(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-spy="scroll"]'), function(element){ new ScrollSpy(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="tab"]'), function(element){ new Tab(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="tooltip"]'), function(element){ new Tooltip(element) });
},false);
vendor/assets/javascripts/
Finally, I moved my application javascript tag to the <head>
and load it asynchronously without tuborlinks-eval and with turbolinks-track. This configures the Javascript to run once on the initial page load. The turbolinks:load
listener is called on every turbolinks page visit and attaches the Bootstrap Native event listeners to the appropriate components in the DOM.
app/views/layouts/application.html.erb
...
<head>
...
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload', 'data-turbolinks-eval': 'false', async: true %>
</head>
...
As of this writing, this solution is in a production Rails 5.1 app.
Incidentally, all of Javascript listed in application.js, when concatenated and minified, is less than 20KB. By contrast, jQuery by itself is 86KB minified. Bootstrap Native can significantly improve download times for your app.