Cannot read property 'top' of undefined Jquery/javascript - javascript

Probably this question has been answered many times before, but i can't find anything that related to my code.
Everything works properly, when the menu nav is open, etc. the smooth scrolling works as well, except when i click the arrow-down to go the the next section, smooth scrolling doesnt work.
I have been looking at it and trying to figure it out for a while but i am unable to do so.
I am still learning jquery and javascript.
A full DEMO of this code in use can be found HERE.
Open dev tools and you will see the errors in the console.
EDIT
added..
.arrow-down-wrapper a[href^="#"]
to
$('nav.mobile-nav a[href^="#"], .arrow-down-wrapper a[href^="#"]').on('click', function (e) {
...
}
Smooth scrolling not works for the 'arrow-down', but i am still getting
'Uncaught TypeError: Cannot read property 'top' of undefined'
console.log(target); outputs the correct targets. #Home, #about, etc..
This is the code i have:
//smooth transition to sctions when links in header are clicked
function onScroll(event){
var scrollPosition = $(document).scrollTop();
$('nav.mobile-nav a').each(function () {
var currentLink = $(this);
var refElement = $(currentLink.attr("href"));
if (refElement.position().top <= scrollPosition && refElement.position().top + refElement.height() > scrollPosition) {
$('nav.mobile-nav a').removeClass("current");
currentLink.addClass("current");
}
else{
currentLink.removeClass("current");
}
});
}
$(document).ready(function () {
$(document).on("scroll", onScroll);
$('nav.mobile-nav a[href^="#"]').on('click', function (e) {
e.preventDefault();
$(document).off("scroll");
$('nav.mobile-nav a').each(function () {
$(this).removeClass('current');
});
$(this).addClass('current');
var target = this.hash;
$target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top
}, 1000, 'swing', function () {
window.location.hash = target;
$(document).on("scroll", onScroll);
});
});
});

The issue was that the code wasn't specific enough. The loop was iterating through all the items in the list, that is all the links that where #tags, and links to other pages. That is the reason i was getting the error of top not defined, that item it was looking for didn't exist. a[href^="#"' after adding that, loop only iterated the items with # ID tags.
Commented the changes i made
//smooth transition to sctions when links in header are clicked
function onScroll(event){
var scrollPosition = $(document).scrollTop();
$('nav.mobile-nav a[href^="#"').each(function () { //added a[href^="#"] so that the loop only iterates over the elements with the ID tag
var currentLink = $(this);
var refElement = $(currentLink.attr("href"));
console.log(currentLink.attr("href")); //log
if (refElement.position().top <= scrollPosition && refElement.position().top + refElement.height() > scrollPosition) {
$('nav.mobile-nav a').removeClass("current");
currentLink.addClass("current");
}
else{
currentLink.removeClass("current");
}
});
}
$(document).ready(function () {
$(document).on("scroll", onScroll);
$('nav.mobile-nav a[href^="#"], .arrow-down-wrapper a[href^="#"]').on('click', function (e) {
e.preventDefault();
$(document).off("scroll");
$('nav.mobile-nav a').each(function () {
$(this).removeClass('current');
});
$(this).addClass('current');
var target = this.hash;
$target = $(target);
console.log(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top - 100
}, 1000, 'swing', function () {
window.location.hash = target;
$(document).on("scroll", onScroll);
});
});
});

That is a style so the value, in javascript, would need to be .style.top not just .top. That is why you are getting 'undefined'. There is no top property assigned to the object you are using.
I don't use jQuery but, in javascript, to retrieve the top property, you could do something like:
var a = document.getElementsByTagName('div')[0];
var b = a.style.top;
console.log(parseInt(b,10)); //cause b will be equal to 'nnpx' and parseInt removes the 'px'
However, this doesn't read any top value set in CSS. You need to set the value with javascript in order to read that. There is a way to read the value set in a CSS stylesheet but I don't recall how.

I don't actually get the problem.
Opening the dev tools i have some errors but the smooth scroll on your website works quite fine for me.
Have a look at this jsfiddle i made:
https://jsfiddle.net/p4x3d2dn/1/
HTML
<ul class="nav">
<li><a class="scroll" href="#section_home">Home</a></li>
<li><a class="scroll" href="#section_about">About</a></li>
<li><a class="scroll" href="#section_team">Team</a></li>
<li><a class="scroll" href="#section_gallery">Gallery</a></li>
<li><a class="scroll" href="#section_contact">Contact</a></li>
</ul>
<section id="section_home">
<h1>Home</h1>
</section>
<section id="section_about">
<h1>About</h1>
</section>
<section id="section_team">
<h1>Team</h1>
</section>
<section id="section_gallery">
<h1>Gallery</h1>
</section>
<section id="section_contact">
<h1>Contact</h1>
</section>
Adding other links (such as a scroll down arrow) is just a matter of adding the correct href attribute to the .scroll tag:
<a class="scroll" href="#section_whatever_you_want">
<i class="fa fa-chevron-down"></i>
</a>
A slightly different approach must be taken if you have custom generated sections and you are not in control of the DOM
This is all the javascript you need:
$(document).ready(function() {
$(".scroll").on("click", function() {
//event.preventDefault();
var el = $(this).attr("href");
$('html, body').animate({
scrollTop: $(el).offset().top
}, 2000);
});
});

Related

Getting window.scrollTo(0, 0); to work

I know that this problem got probably asked and solved a thousand times, yet I need advice on my specific case.
I've set several anchors on my website and I'm using jquery to smoothly scroll between them. I've been using a #top anchor to scroll to the top of the page and it works. The problem is, however, that if I let the button scroll to #top it won't scroll to the absolute top of the page (since I can't set the anchor high enough on the webpage).
I tried to get it work with the window.scrollTo(0, 0); command, but I don't know how to get it to work while still having the possibility to scroll to anchors.
I used the following jQuery for the anchor-scrolling:
$(document).ready(function(){
$('a[href^="#"]').on('click',function (e) {
e.preventDefault();
var target = this.hash;
var $target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top
}, 900, 'swing', function () {
window.location.hash = target;
});
});
});
With this HTML:
<a href="#top">
<img alt="" heigth="60" onmouseout="this.src='http://i.imgur.com/0JvWWER.png'" onmouseover="this.src='http://i.imgur.com/Ow7CVn0.png'" src="http://i.imgur.com/0JvWWER.png" width="60" />
</a>
Now, how do I get the window.scrollTo(0,0); to work and how do I implement it in the html body?
Thanks in advance.
As somethinghere says, do a small change here:
$(document).ready(function(){
$('a[href^="#"]').on('click',function (e) {
e.preventDefault();
var target = $(this).attr("href");
$('html, body').stop().animate({
// Change here...
'scrollTop': ((target === '#top') ? 0 : $(target).offset().top)
}, 900, 'swing', function () {
window.location.hash = target;
});
});
});

After adding a "current" class to an active item in a menu using jQuery, how can css changes be made to the container of the item?

I have added an "active" class to the current item in a menu bar and have implemented a Smooth Scroll feature to it as well. ( http://jsfiddle.net/T98VG/ )
I want the background color of the item to change in the menu bar but this doesn't seem to be working properly. So far, the background color of the link in the menu bar changes, not the container the link is in. How can I implement this?
jQuery code used to add "active" state to current item (this works fine):
$(document).ready(function () {
$(document).on("scroll", onScroll);
//smoothscroll
$('#nav a[href^="#"]').on('click', function (e) {
e.preventDefault();
$(document).off("scroll");
$('#nav a').each(function () {
$(this).removeClass('current');
});
$(this).addClass('current');
var target = this.hash,
menu = target;
$target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top-0
}, 1000, 'swing', function () {
window.location.hash = target;
$(document).on("scroll", onScroll);
});
});
});
function onScroll(event){
var scrollPos = $(document).scrollTop();
$('#nav a').each(function () {
var currLink = $(this);
var refElement = $(currLink.attr("href"));
if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
$('#nav a').removeClass("current");
currLink.addClass("current");
}
else{
currLink.removeClass("current");
}
});
}
Here's a JSFiddle with the code I have so far and the problem ( http://jsfiddle.net/T98VG/ ). In the menu bar with 4 items: #1, #2, #3 and #4, only the background color of the active link gets highlighted. I'd like the box "nav li" to be filled with the color as well.
I'm new to coding so all help is welcome. Thank you!
You can use the parent() call on each link, and add the current class to the parent of the links (the <li>'s) instead of the links themselves.
See the updated JSFiddle

jquery issues when changing active navigation class on scroll and on click

I'm doing a single page where navigation .active class should change both on scroll and on click. I'm also changing class of header when scrolling down (".large" and ".small"), but this two scripts somehow don't work with each other.
For header resizing I'm doing this:
$(document).on("scroll",function(){
if($(document).scrollTop()>200){
$('header').removeClass('large').addClass('small');
} else{
$('header').removeClass('small').addClass('large');
}
});
For change active class I'm doing this:
$(document).ready(function () {
$(document).on("scroll", onScroll);
//smoothscroll
$('a[href^="#"]').on('click', function (e) {
e.preventDefault();
$(document).off("scroll");
$('a').each(function () {
$(this).removeClass('active');
})
$(this).addClass('active');
var target = this.hash,
menu = target;
$target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top+2
}, 500, 'swing', function () {
window.location.hash = target;
$(document).on("scroll", onScroll);
});
});
});
function onScroll(event){
var scrollPos = $(document).scrollTop();
$('#top-menu a').each(function () {
var currLink = $(this);
var refElement = $(currLink.attr("href"));
if (refElement.position().top <= scrollPos && refElement.
position().top + refElement.height() > scrollPos) {
$('#top-menu ul li a').removeClass("active");
currLink.addClass("active");
}
else{
currLink.removeClass("active");
}
});
}
This works perfectly when scrolling, but clicking on a top nav link just crashes "header" function and it doesn't resize anymore. Can anyone see what's the problem is?
As f00bar mentioned, $(document).off("scroll"); removes the eventhandler. The scroll event wont be fired anymore. so your top code wont run after a click.

how to solve this Jquery error?

I have made some Jquery that you can see with this fiddle below:
http://madaxedesign.co.uk/dev/Test/
http://jsfiddle.net/x82mU/1/
Code:
$(document).ready(function() {
var $root = $('html, body ');
$('.scroll a').click(function(e) {
var href = $.attr(this, 'href');
$root.animate({
scrollTop: $(href).offset().top
}, 500, function () {
window.location.hash = href;
});
return false;
});
// Responsive menu
$(function() {
var pull = $('#pull'),
menu = $('nav ul'),
menuHeight = menu.height()
$(pull).on('click', function(e) {
e.preventDefault();
menu.slideToggle();
});
$(window).resize(function(){
var w = $(window).width();
if(w > 320 && menu.is(':hidden')) {
menu.removeAttr('style');
}
});
});
});
But it pulls through with this error:
Uncaught TypeError: Cannot read property 'top' of undefined
This is preventing my next piece of Jquery to work.
I was wondering if anyone could let me know why, or give me a solution?
Many Thanks
You are trying to get the selector from href which isn't there in many of your menu items.
i.e:
<li>Home</li>
<li>About us</li>
<li>Portfolio</li>
<li>Contact us</li>
and
$(href).offset().top //here offset() of an empty jquery object is undefined.
Not an issue but you can just do this.href instead $.attr(this, 'href')
Try this:
$('.scroll a').click(function(e) {
var href = $(this).attr("href"), $el = $(href), top=0; //default top to 0
if($el.length) //if there is element matching the href
top = $el.offset().top; //set the top
$root.animate({
scrollTop: top //now scroll
}, 500, function () {
window.location.hash = href;
});
return false;
});
Fiddle
var href = $(this).attr('href');
update as comment
for
scrollTop: $(href).offset().top
to work, the
href
variable has to be an element on page.
so if your link is like
...
it will be fine.
jquery dom object creation
$(dom_element)
targets html tags, classes, id's or existing Dom objects ( window, document .. )

How to add smooth scrolling to Bootstrap's scroll spy function

I've been trying to add a smooth scrolling function to my site for a while now but can't seem to get it to work.
Here is my HTML code relating to my navigation:
<div id="nav-wrapper">
<div id="nav" class="navbar navbar-inverse affix-top" data-spy="affix" data-offset-top="675">
<div class="navbar-inner" data-spy="affix-top">
<div class="container">
<!-- .btn-navbar is used as the toggle for collapsed navbar content -->
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<!-- Everything you want hidden at 940px or less, place within here -->
<div class="nav-collapse collapse">
<ul class="nav">
<li>Home</li>
<li>Services</li>
<li>Contact</li>
</ul><!--/.nav-->
</div><!--/.nav-collapse collapse pull-right-->
</div><!--/.container-->
</div><!--/.navbar-inner-->
</div><!--/#nav /.navbar navbar-inverse-->
</div><!--/#nav-wrapper-->
Here is the JS code I've added:
<script src="js/jquery.scrollTo-1.4.3.1-min.js"></script>
<script>
$(document).ready(function(e) {
$('#nav').scrollSpy()
$('#nav ul li a').bind('click', function(e) {
e.preventDefault();
target = this.hash;
console.log(target);
$.scrollTo(target, 1000);
});
});
</script>
For what it's worth, here is where I received info on what I've done so far, and here is my site in it's current form. If you can help me I'll bake you a pie or cookies or something. Thanks!
Do you really need that plugin? You can just animate the scrollTop property:
$("#nav ul li a[href^='#']").on('click', function(e) {
// prevent default anchor click behavior
e.preventDefault();
// store hash
var hash = this.hash;
// animate
$('html, body').animate({
scrollTop: $(hash).offset().top
}, 300, function(){
// when done, add hash to url
// (default click behaviour)
window.location.hash = hash;
});
});
fiddle
If you have a fixed navbar, you'll need something like this.
Taking from the best of the above answers and comments...
$(".bs-js-navbar-scrollspy li a[href^='#']").on('click', function(event) {
var target = this.hash;
event.preventDefault();
var navOffset = $('#navbar').height();
return $('html, body').animate({
scrollTop: $(this.hash).offset().top - navOffset
}, 300, function() {
return window.history.pushState(null, null, target);
});
});
First, in order to prevent the "undefined" error, store the hash to a variable, target, before calling preventDefault(), and later reference that stored value instead, as mentioned by pupadupa.
Next. You cannot use window.location.hash = target because it sets the url and the location simultaneously rather than separately. You will end up having the location at the beginning of the element whose id matches the href... but covered by your fixed top navbar.
In order to get around this, you set your scrollTop value to the vertical scroll location value of the target minus the height of your fixed navbar. Directly targeting that value maintains smooth scrolling, instead of adding an adjustment afterwards, and getting unprofessional-looking jitters.
You will notice the url doesn't change. To set this, use return window.history.pushState(null, null, target); instead, to manually add the url to the history stack.
Done!
Other notes:
1) using the .on method is the latest (as of Jan 2015) jquery method that is better than .bind or .live, or even .click for reasons I'll leave to you to find out.
2) the navOffset value can be within the function or outside, but you will probably want it outside, as you may very well reference that vertical space for other functions / DOM manipulations. But I left it inside to make it neatly into one function.
$("#YOUR-BUTTON").on('click', function(e) {
e.preventDefault();
$('html, body').animate({
scrollTop: $("#YOUR-TARGET").offset().top
}, 300);
});
// styles.css
html {
scroll-behavior: smooth
}
Source: https://www.w3schools.com/howto/howto_css_smooth_scroll.asp#section2
If you download the jquery easing plugin (check it out),then you just have to add this to your main.js file:
$('a.smooth-scroll').on('click', function(event) {
var $anchor = $(this);
$('html, body').stop().animate({
scrollTop: $($anchor.attr('href')).offset().top + 20
}, 1500, 'easeInOutExpo');
event.preventDefault();
});
and also dont forget to add the smooth-scroll class to your a tags like this:
<li>About Us</li>
I combined it, and this is the results -
$(document).ready(function() {
$("#toTop").hide();
// fade in & out
$(window).scroll(function () {
if ($(this).scrollTop() > 400) {
$('#toTop').fadeIn();
} else {
$('#toTop').fadeOut();
}
});
$('a[href*=#]').each(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')
&& location.hostname == this.hostname
&& this.hash.replace(/#/,'') ) {
var $targetId = $(this.hash), $targetAnchor = $('[name=' + this.hash.slice(1) +']');
var $target = $targetId.length ? $targetId : $targetAnchor.length ? $targetAnchor : false;
if ($target) {
var targetOffset = $target.offset().top;
$(this).click(function() {
$('html, body').animate({scrollTop: targetOffset}, 400);
return false;
});
}
}
});
});
I tested it and it works fine. hope this will help someone :)
What onetrickpony posted is okay, but if you want to have a more general solution, you can just use the code below.
Instead of selecting just the id of the anchor, you can make it bit more standard-like and just selecting the attribute name of the <a>-Tag. This will save you from writing an extra id tag. Just add the smoothscroll class to the navbar element.
What changed
1) $('#nav ul li a[href^="#"]') to $('#nav.smoothscroll ul li a[href^="#"]')
2) $(this.hash) to $('a[name="' + this.hash.replace('#', '') + '"]')
Final Code
/* Enable smooth scrolling on all links with anchors */
$('#nav.smoothscroll ul li a[href^="#"]').on('click', function(e) {
// prevent default anchor click behavior
e.preventDefault();
// store hash
var hash = this.hash;
// animate
$('html, body').animate({
scrollTop: $('a[name="' + this.hash.replace('#', '') + '"]').offset().top
}, 300, function(){
// when done, add hash to url
// (default click behaviour)
window.location.hash = hash;
});
});
with this code, the id will not appear on the link
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});

Categories

Resources