Using # in anchors for javascript functionality - javascript

Like most things in CSS, there are ways that work, and there are the right ways. Unfortunately, I'm not so much of a CSS guru.
I set '#' I my hrefs for a number of things - for things like opening menus etc.
However, I recently found a need to scroll smoothly to a place on the page. After some investigation, I came up with this code:
$('a[href^="#"]').click(function (e) {
e.preventDefault();
$(".body-wrap").animate({
scrollTop: $(this.hash).offset().top
}, 300);
});
Nothing wrong with it - it works.
However, it also causes conflict with every other '#' based href on the page that I use for other javascript triggers - specifically a[href^="#"].
The question I have is, is there a better way to approach this that is still as generic? For instance, you might say - don't need to assign # to all hrefs - I'm not sure what the impact might be, or there might be ways of adding to the selector above to make it more specific - such as, starts with #,but has other characters following.
This kind of thing must challenge developers every day, so there must be preferred techniques, or patterns to deal with this cleanly.

My preferred solution is to give them a class
$(".scrollLink").on("click",function (e) {
e.preventDefault();
$(".body-wrap").animate({
scrollTop: $(this.hash).offset().top
}, 300);
});

The best practice is prefixing javascript specific classes with js-.
$(document).on('click', '.js-scroll-link', function(e) {
e.preventDefault();
$('.body-wrap').animate({
scrollTop: $(this.hash).offset().top
}, 300);
});

If you don't want/need to use classes, you can always check if this.hash is an empty string before animating. In that case you will need to add empty/invisible <a id="top"></a> to the top of your page and set href to #top for your "smooth scroll links" which are meant to scroll to the top of page.
However using classes is preferable.

Related

jQuery .scrollTop not scrolling properly

So I've got the following code:
$(document).ready(function(){
if(window.location.hash) {
$('body,html').animate({
scrollTop: $(window.location.hash).offset().top
}, 1000);
}
})
Which I've built with the help of code taken from StackOverflow.
I call the page at url#destination so actually it should scroll to the element whose ID is the page Hash. The element exists and the page scrolls down, but not to the exact element offset, but a bit more above. It could be just fine but I want it to work as I expected.
So I now show you the console results:
>>>$("body").scrollTop()
>1155
>>>$("#aboutus").offset().top
>1672.890625
Could someone explain this to me? Because I cannot understand anything here.
Hmmm... It works fine for me. Maybe the problem is, as a user pointed in the comments, the elements haven't loaded yet so you should use $(window).load(). But if you use that, your code won't work fine since the browsers have the built-in method that when a hash exists in the url, it goes directly wherever the element whose id is the hash is. This happens because this action is triggered before the .load event detection in your javascript code. So, if you want to make sure the code works, replace the targeting of the element with other attribute like:
$(window).on("load", function(){
if(window.location.hash) {
setTimeout(function(){
$('body,html').animate({
scrollTop: $('*[idt="'+(window.location.hash).replace("#", "")+'"]').offset().top
}, 1000);
}, 130)
}
})
This should make fully sure the animation works properly, since there's no element that has got such a hash, and the js code manages the same way.
The reason it doesn't scroll to the very bottom is because your last element hasn't been added to the DOM at the time of scrollTop execution.
Make scrollTop asynchronous to wait until the DOM has completely rendered:
setTimeout(() => {
element.scrollTop = element.scrollHeight;
}, 0);

Tab content dissapearing

This is a very specific question that I cannot figure out for the death of me.
On this site: http://www.telcogreen.com.au/voice_new
For some reason the content of tab 3 dissapears if I add secondary set of tabs in tab 2. However, if I reverse them, I can have secondary tabs on 3 and the ones on 4 will still display. It's just tab 2 that causes the issue.
Can somebody please point me in the right direction?
Thanks!
It seems that 3rd section doesn't render, because you try to bind click to wrong selector.
Here is your code:
/***** voice page hosted c*/
$('a.hosted').bind('click', function () {
$('#tabs ul li').removeClass('tab-current');
$('#hosted').parents('li').addClass('tab-current');
$('#section-1').removeClass('content-current');
$('#section-3').addClass('content-current');
$("html, body").animate({ scrollTop: 570 }, "slow");
return false;
});
There are no a tags with neither class nor id hosted in the rest of markup.
And I would (if using jquery 1.9.1) get rid of .bind() handler. You use .bind() method, which is a little deprecated. I think it's better to write $('_selector_').click(function () {... instead of $('_selector_').bind('click', function () {... And I'd consider an idea of replacing addClass...removeClass...addClass...removeClass chains with toggleClass.
Anyway, if you want find out what is wrong, learn debugger and watch a breakpoints.
Here is a objects tree of your tabs on the pic:

Tweak smooth scrolling code

I am trying to learn jQuery and I'm having a mental blank at the moment. The following code scrolls my page to the top with a smooth transition, but I would like this smooth transition to work for all anchor/ID links on my site (it's only a one pager).
$(document).ready(function() {
$('a[href="#the-top"]').click(function (e) {
$("html, body").animate({ scrollTop: $('#the-top').offset().top }, 1000);
return false;
});
});
How can I change my code to achieve this?
jQuery(function($) {
$('a[href^=#]').bind('click', function (evt) {
var $what = $('#' + $(this).attr('href').split('#')[1]);
$('html, body').stop().animate({ scrollTop: $what.offset().top }, 1000);
evt.preventDefault();
});
});
Changes suggested in this code:
Change global $ object to jQuery
Just jQuery(fn) as document.ready(fn)
Closure: use jQuery as $ inside that function
Prevent default event from anchor instead of return false (source: http://fuelyourcoding.com/jquery-events-stop-misusing-return-false/)
Use of $what asking for the #something part of anchor href, in order to prevent misbehaviors in IE (because if you have href="#some" sometimes it become href="http://yoursite.com/yourcurrentpage/#some instead)
All of these are kind of optional. You get the idea. Feel free to change.
DEMO AT: http://jsfiddle.net/Nm3cT/
Take a look at Chris Coyier's smooth Scrolling script. It does exactly that and needs no further configuration. Plus, it updates the address on the browser.

How can I tell if a page has jumped to the anchor (#) in JavaScript?

I have some JavaScript that can appear on many different pages. Sometimes those pages have been accessed via a URL containing an anchor reference (#comment-100, for instance). In those cases I want the JavaScript to delay executing until after the window has jumped. Right now I'm just using a delay but that's pretty hackish and obviously doesn't work in all cases. I can't seem to find any sort of DOM event that corresponds to the window "jump".
Aside from the simple delay, the only solution I've come up with is to have the JS look for the anchor in the URL and, if it finds one, watch for changes in scrollTop. But that seems buggy, and I'm not 100% sure that my script will always get fired before the scrolling happens so then it would only run if the user manually scrolled the page. Anyhow, I don't really like the solution and would prefer something more event driven. Any suggestions?
Edit to clarify:
I'm not trying to detect a hash change. Take the following example:
Page index.php contains a link to post.php#comment-1
User clicks the link to post.php#comment-1
post.php#comment-1 loads
$(document).ready fires
Not long later the browser scrolls down to #comment-1
I'm trying to reliably detect when step 5 happens.
You can check window.onhashchange in modern browsers. If you want cross compatible, check out http://benalman.com/projects/jquery-hashchange-plugin/
This page has more info on window.onhashchange as well.
EDIT: You basically replace all anchor names with a similar linking convention, and then use .scrollTo to handle the scrolling:
$(document).ready(function () {
// replace # with #_ in all links containing #
$('a[href*=#]').each(function () {
$(this).attr('href', $(this).attr('href').replace('#', '#_'));
});
// scrollTo if #_ found
hashname = window.location.hash.replace('#_', '');
// find element to scroll to (<a name=""> or anything with particular id)
elem = $('a[name="' + hashname + '"],#' + hashname);
if(elem) {
$(document).scrollTo(elem, 800,{onAfter:function(){
//put after scroll code here }});
}
});
See jQuery: Scroll to anchor when calling URL, replace browsers behaviour for more info.
Seems like you could use window.onscroll. I tested this code just now:
<a name="end" />
<script type="text/javascript">
window.onscroll = function (e) {
alert("scrolled");
}
</script>
which seems to work.
Edit: Hm, it doesn't work in IE8. It works in both Firefox and Chrome though.
Edit: jQuery has a .scroll() handler, but it fires before scrolling on IE and doesn't seem to work for Chrome or Firefox.
To detect when the element appears on the screen, use the appear plugin:
$('#comment-1').appear(function() {
$(this).text('scrolled');
});

Mootools problem when zoomed in

I am using Mootools extensively for a site which I am developing. But recently I noticed a problem that the animations slow down alot when I zoom (using the browsers Zoom In) in into the site. What could be a possible reason for this problem ? Or is this problem inherit in Mootools itself. This happens in Chrome 6.0.472 as well as Firefox 3.6.8.
Thanks,
Nitin
many things are wrong here with regards to speed optimisations.
lets take a look at this mouseover code that seems to slow down:
this.childNodes.item(1).style.left="0px";
this.getElements('div').setStyles({'opacity':'1'});
this.getElements('div').set('morph', {duration:'normal',transition: 'sine:out'});
this.getElements('span').set('morph', {duration:'normal',transition: 'sine:out'});
this.getElements('div').morph({'left':'-28px'});
this.getElements('span').morph({'left':'-30px','color':'#FFF'});
obviously this will work as it does but it's so very wrong i don't know where to begin.
the idea is to abstract and setup the repetitive tasks so they are done as a one off.
consider line by line the code above:
this.childNodes.item(1).style.left="0px";
this is wrong for a mootools app anyway, it would need to be this.getFirst().setStyle("left", 0);
the this.getFirst() is a lookup, it should be cached - although that's not a slow one.
then comes the bad part.
you select all child divs 3 times and all spans twice, where NO SELECTION should be applicable. VERY EXPENSIVE
you reset the Fx.morph options every mouseover event where there are no changes (although you seem to have a different duration for mouseenter and mouseleave - this is expensive)
consider this code:
[document.id("menu1"), document.id("menu2")].each(function(el) {
// use element storage to save lookups during events
el.store("first", el.getFirst());
el.store("divs", el.getElements("div"));
el.store("spans", el.getElements("span"));
// store the fx.morph options once and for all, no need to do so
// on every event unless you are changing something
el.retrieve("divs").set("morph", {
duration: 'normal',
transition: 'sine:out',
link: 'cancel'
});
el.retrieve("spans").set("morph", {
duration: 'normal',
transition: 'sine:out',
link: 'cancel'
});
// add the events
el.addEvents({
mouseenter: function(e) {
// retrieve the saved selectors from storage and do effects
this.retrieve("first").setStyle("left", 0);
this.retrieve("divs").morph({
"left": -28
});
this.retrieve("spans").morph({
'left': '-30px',
'color': '#FFF'
});
}
});
});
it will save a lot of processing on the events.
similarly, there are plenty of places where you are not really using the mootools api.
document.getElementById(curr).style.cursor="pointer";
$(this).removeEvents -> no need for $, this is not jquery.
document.getElementById("lightbox").style.visibility="hidden";
m=setTimeout('gallery()',5000); --> use the mootools var timer = (function() { ... }).delay(5000);, don't use strings with setTimeout/interval as it forces eval and reflows but pure anon functions
etc etc.
you really can make a day out of refactoring all this and making it 'nice' but it's going to be worth it.
also, learn about chaining
$("ribbon").set('morph', {duration:'long',transition: 'bounce:out'});
$("ribbon").morph({'top':'-10px'});
$("ribbon").addEvents({
this is calling up a selector 3 times. instead you can:
store it. var ribbon = $("ribbon"); ribbon.set...
chain it. $("ribbon").set("morph", {duration: 500}).morph({top:-10}).addEvents() - mootools element methods tend to return the original element so you can take the response of the last function and apply more to it.
1 is better for readibility, 2 is faster to do.
also. you have way too many global variables which makes your scope chain lookups more expensive, this will affect many call ups and places. try to namespace properly, if you need to access real global vars from functions and closures, use window.varname etc etc.
Another possible improvement here would be the use of event delegation (event bubbling will cause events to fire up the dom tree to the parents, mootools has an api to deal with it so you can add singular events to parent elements and not have to attach nnn events to all children) - look it up.
P.S. please don't take this in the wrong way - it's not meant to rubbish your work and it's just some constructive (i hope) advice that can help you bring it to the next level. good luck :)
I haven't seen any specific code in MooTools or any other library that checks if browser is zooming during animation, so I think that animation slows down, since browser using more CPU for computing zooming process.

Categories