I have a notification dropdown menu being fired by adding a CSS class via Javascript. However, I am certain that Turbolinks is causing it to not work properly, as it seems to only work on refresh.
There is a data-turbolinks="true"
in the <body>
tag of the document. If I change this to false
I get normal operation.
If I put the data-turbolinks="false"
in the <div>
tag of the specific links it still does not work properly.
So I am thinking I will have to change the JaveScript so it is not affected by the Turbolinks. However, I am not sure how to go about this. This is for a Laravel 5.6 app.
/*--------------------------------------------------*/
/* Notification Dropdowns
/*--------------------------------------------------*/
$(".header-notifications").each(function() {
var userMenu = $(this);
var userMenuTrigger = $(this).find('.header-notifications-trigger a');
$(userMenuTrigger).on('click', function(event) {
event.preventDefault();
if ( $(this).closest(".header-notifications").is(".active") ) {
close_user_dropdown();
} else {
close_user_dropdown();
userMenu.addClass('active');
}
});
});
// Closing function
function close_user_dropdown() {
$('.header-notifications').removeClass("active");
}
// Closes notification dropdown on click outside the conatainer
var mouse_is_inside = false;
$( ".header-notifications" ).on( "mouseenter", function() {
mouse_is_inside=true;
});
$( ".header-notifications" ).on( "mouseleave", function() {
mouse_is_inside=false;
});
$("body").mouseup(function(){
if(! mouse_is_inside) close_user_dropdown();
});
// Close with ESC
$(document).keyup(function(e) {
if (e.keyCode == 27) {
close_user_dropdown();
}
});
I think the issue is that the script only selects the elements on the first page load, rather than on every page load. For example, calling $(".header-notifications")
will attempt to find all the elements with a class of .header-notifications
, however this is only run once, so when a new page is loaded with Turbolinks , the body
gets replaced, and those selected elements no longer exist. Scripts are not executed again until a full page load, and so this script is only run once—.header-notifications
elements are never reselected.
To fix this, the Turbolinks README recommends using event delegation:
When possible, avoid using the turbolinks:load event to add other event listeners directly to elements on the page body. Instead, consider using event delegation to register event listeners once on document or window.
So you add your event listeners to the document or window, then choose which elements it should run on with a selector, e.g.:
$(document).on(
"click", ".header-notifications-trigger a", function (event) { … }
)
This means that any time a .header-notifications-trigger a
element is added to the page, the click event handler will be fired.
With that in mind, you may wish to update your script to something like:
/*--------------------------------------------------*/
/* Notification Dropdowns
/*--------------------------------------------------*/
// For convenience and to prevent unnecessary $() calls
var doc = $(document);
doc.on("click", ".header-notifications-trigger a", function (event) {
event.preventDefault();
var user_menu = $(this).closest(".header-notifications");
if (user_menu.is(".active")) {
close_user_dropdown();
} else {
close_user_dropdown();
user_menu.addClass('active');
}
});
// Closing function
function close_user_dropdown() {
$('.header-notifications').removeClass("active");
}
// Closes notification dropdown on click outside the container
var mouse_is_inside = false;
doc.on("mouseenter", ".header-notifications", function() {
mouse_is_inside = true;
});
doc.on("mouseleave", ".header-notifications", function() {
mouse_is_inside = false;
});
doc.on("mouseup", function(){
if(!mouse_is_inside) close_user_dropdown();
});
// Close with ESC
doc.on("keyup", function(e) {
if (e.keyCode == 27) {
close_user_dropdown();
}
});