Reputation: 1193
I'm doing an SPA from scratch and so far I've gotten everything working except the active
state off the navigation links will not update according to where user is navigating to.
The funny thing is that when I open up the Developer Tools in Chrome, under "Elements" the DOM is still showing the active
class on the original nav element, but under "Console", when I console.log the element, it shows that the class has been removed.
I've tried event.stopPropagation()
in a few places and the jQuery.off()
function but nothing seems to fix this problem.
Problem: The class list of the active and the former active link doesn't update when clicking on a navigation link, but the console.log function does show an updated classlist off the former active link
Desired Behaviour: That the former active link losses its active
class and that the currently active link gets an active
class after its navigation link has been clicked and the page renders the new content(the supposedly new page).
UPDATE 1: Updated the code according to suggestions and it got the first part of my problem working. Removing active
class from all li
elements now works, but making the page render the updated links with active
class on currently active link after navigation does not work. See in app.js under generateView() function.
UPDATE 2: Got it working. See on the bottom of this Question. Thank you!
My app.js
(function ($, hbs){
'use strict';
// declare some usefull variables to use throughout the app
var
doc = document,
oldPath = '',
newPath = '',
navParentEl = '';
// 1: Manually trigger a hashchange to start the app.
window.onload = function (e) {
$(window).trigger('hashchange');
};
// 2: Catch clicks on the root-level element for anchor tag clicks.
doc.body.addEventListener('click', function (e) {
//e.stopPropagation();
var tag = e.target;
// check element clicket
if (tag.tagName === 'A' && tag.href && e.button === 0) {
// it's a left-click on an anchor. Lets navigate!
if (tag.origin === window.location.origin) {
// prevent the page from navigating
e.preventDefault();
// it's a link within the site, HURRAY!
oldPath = window.location;
newPath = tag.href,
navParentEl = tag.parentElement;
console.log(navParentEl);
// Update the URL bar! IMPORTANT!
// @TODO: MOVE INTO A FUNCTION OR OBJECT
window.history.pushState(null, '', newPath);
render(window.location.hash, data, e);
}
}
});
// register Handlebars partials
hbs.registerPartial({
'header': hbs.templates.header,
'footer': hbs.templates.footer
});
$(window).on('hashchange', function (e) {
// On every hash change the render function is called with the new hash.
render(window.location.hash, data, e);
});
function render(url, data, evt) {
var temp = url.split('/')[0];
// Hide current page
$('.pages').addClass('hidden');
// remove anchors .active class
//$('.nav-parent').removeClass('active');
var map = {
'': function (data) {
renderPage('home', data);
},
'#home': function (data) {
renderPage('home', data);
},
'#gallery': function (data) {
renderPage('gallery', data);
},
'#about': function (data) {
renderPage('about', data);
}
};
if (map[temp]) {
map[temp](data);
} else {
renderErrorPage(data);
}
}
function renderPage(page, data) {
var tpl = hbs.templates[page](data);
generateView(tpl, page);
}
function renderErrorPage(data) {
renderPage('error', data);
}
function generateView(tpl, page) {
var pageId = '#' + page;
$(pageId).removeClass('hidden');
$('.container').html(tpl);
// this works for removing the active class from all li elements
$('.nav-parent').removeClass('active');
// add .active class to the new active anchor element
// does not work
$(navParentEl).addClass('active');
}
})(jQuery, Handlebars);
My navigation HTML:
<div class="header clearfix">
<nav>
<ul class="nav nav-pills pull-right">
<li role="presentation" class="nav-parent active"><a href="#home" class="links">Home</a></li>
<li role="presentation" class="nav-parent"><a href="#gallery" class="links">Gallery</a></li>
<li role="presentation" class="nav-parent"><a href="#about" class="links">About</a></li>
<li role="presentation" class="nav-parent"><a href="#contact" class="links">Contact</a></li>
</ul>
</nav>
<h3 class="text-muted">{{ projectName }}</h3>
</div>
UPDATE 2: Got it working after some tips. I needed to re-think my generateView()
function. Here's the final code for that function:
function generateView(tpl, page) {
var pageId = '#' + page;
// remove hidden class from content to be shown
$(pageId).removeClass('hidden');
// add the template to the html
$('.container').html(tpl);
// move the active class from the former active link
$('.nav-parent').removeClass('active');
// get the current hash of the location
var newHash = window.location.hash,
// get all links
_links = document.querySelectorAll('.links'),
currentActiveLink = '';
// iterate over the _links object and find the link with href === newHash
for ( var i = 0; i < _links.length; i++ ) {
if ( _links[i].getAttribute('href') === newHash ) {
// store the link with href == newHash
// inside the currentActiveLink variable
currentActiveLink = _links[i];
}
}
// add active class to current active link
currentActiveLink.parentElement.classList.add('active');
}
Thank you!
Upvotes: 1
Views: 1960
Reputation: 87
isnt it possible that you redraw your navigation? didnt really go through your code but it took me a looong time to discover that in my SPI. I changed some params, but I also ajax-loaded those elements wit default serverside properties... EDIT: yea I can see that happening in your app
Upvotes: 1