Wappler Version : v4.6.4
Operating System : MacOS
Server Model: NodeJS
Database Type: N/A
Hosting Type: Docker
Expected behavior
dmxBootstrap5Navigation.js correctly appends the class “active”
Actual behavior
dmxBootstrap5Navigation.js correctly appends the class “active”, but the changes are lost as soon as the HTML fragment is loaded on links with Internal routing enabled (SPA-style). This only happens on projects that have a navigation bar (e.g.: sidebar) loaded through a fragment - the top navigation bar works as intended because it’s not loaded again through the fragment
_update() is called before the fragment is loaded (as soon as pushstate), so the addition of class “active” might be lost (overwritten) by the HTML coming from the fragment
How to reproduce
Use your browser’s network tools to bandwidth limit the network connection, so HTTP queries are executed slower. Add console.log("CALLED!") to dmxBootstrap5Navigation.js:
dmxBootstrap5Navigation.js
// Bootstrap 5 Navigation
document.addEventListener('DOMContentLoaded', function(event) {
window.addEventListener('popstate', _update);
window.addEventListener('pushstate', _update);
_update();
function _update() {
console.log("CALLED!")
var url = window.location.href;
document.querySelectorAll('a.nav-link:not([data-bs-toggle]), a.dropdown-item').forEach(function(elem) {
elem.classList.toggle('active', elem.href == url || elem.href == url.split("?")[0].split("#")[0]);
});
document.querySelectorAll('a.dropdown-item.active').forEach(function(elem) {
var theItem = elem.closest('.nav-item.dropdown');
if (theItem) {
var theToggle = theItem.querySelector('.dropdown-toggle');
if (theToggle) {
theToggle.classList.toggle('active');
}
}
});
}
});
Use a NodeJS project with Internal routing links, so page changes are loaded through fragments. Notice the browser’s console and network tab. Once you click the link _update() is called and class “active” is appended, but once the fragment loads those changes are lost.
Suggestion:
_update() needs to be called after fragment load in addition to the current events
Would appreciate if we could get this fixed, an event needs to be fired after the HTML fragment is loaded. I'm not familiar with dmxRouting.js to do it myself
I’ve made several changes to the dmxBootstrap5Navigation.js. The dmx-view component is not the only component that loads html content from the server, also the dmx-route component does this. Also it could be possible that the nav is being generated dynamically (data from serverconnect to build the nav).
So instead of triggering an event for all those different cases I’ve added the Mutation Observer to watch the document for changes in the DOM, when new nodes are added to the document I call the _update. Also moved it outside the DOMContentLoaded listener, when for example the include was in the fragment it would not execute since the DOMContentLoaded event will never trigger then.
Would be fine if you could test this solution. Your solution also works fine for the fragment issue you encountered but doesn’t solve all the issues I described.
I checked the addedNodes because I only wanted to do the _update only when new nodes where added, forgot that the records is also an array. But having it on any change should not have that much impact on performance, so will do the _update on all changes. The records.length is also not needed, it only triggers the callback when there was an actual DOM change and we only listen to childList changes (adding and removing nodes).
Simplified version:
new MutationObserver(_update).observe(document.body, { subtree: true, childList: true });
This is to allow having class active while like on the following URL:
/dashboard/change-password
Because in my website I have a sidebar for the user dashboard, but I want the navigation bar link “Dashboard” to be active on any page within the dashboard