a href links within tabs not doing anything - javascript

I'm using following code for my bootstrap tabs in order to have the #name of the tab in the url, as I want to redirect to a specific tab once some post actions are done, which works fine.
However, ever since I started using the code, all the other links stopped doing anything, it doesn't open the page or display an error, nothing at all.
Here's my code :
$(function() {
var gotoHashTab = function (customHash) {
var hash = customHash || location.hash;
var hashPieces = hash.split('?'),
activeTab = $('[href=' + hashPieces[0] + ']');
activeTab && activeTab.tab('show');
}
// onready go to the tab requested in the page hash
gotoHashTab();
// when the nav item is selected update the page hash
$('.nav a').on('shown.bs.tab', function (e) {
window.location.hash = e.target.hash
//location.hash = $(e.target).attr('href').substr(1);
scrollTo(0,0);
})
// when a link within a tab is clicked, go to the tab requested
$('.tab-pane a').click(function (event) {
if (event.target.hash) {
e.preventDefault();
gotoHashTab(event.target.hash);
}
})
$('.tab-pane a').on('click', function(e){
e.preventDefault()
})
});
I've noticed on some other questions, that e.preventDefault() may cause that, but when I comment those out nothing happens.
How to rewrite the code to keep functionality but make those normal links work again ?

It's looking at all the links with this:
activeTab = $('[href=' + hashPieces[0] + ']');
Change to:
activeTab = $('.tab-pane [href=' + hashPieces[0] + ']');
And also try being more specific with the .nav, since .nav is default for all navs. So this:
$('.nav a').on('shown.bs.tab', function (e) {
Should change to:
$('.tab-pane a').on('shown.bs.tab', function (e) {

Related

Navigation active class not working on server

I developed a navigation bar. I just add an active class for links that highlight the current link. The strange thing is that the feature is working on my local pc perfectly. I just uploaded it to my server and check, but it's not working at all.
This is the uploaded site
This is the script that I used.
$(function () {
var url = window.location;
$('ul.active-link a[href="' + url + '"]').parent().addClass('active');
$('ul.active-link a').filter(function () {
return this.href == url;
}).parent().addClass('active');
});
<script>
$(document).ready(function () {
var location = window.location.href;
$('#nav li a').each(function(){
if(location.indexOf(this.href)>-1) {
$(this).parent().addClass('active');
}
});
});

Add class active when clicking the menu link with Jquery

I have HTML
<div id="top" class="shadow">
<ul class="gprc">
<li>Home</li>
<li>Text1</li>
<li>Text2</li>
<li>Text3</li>
<li>Text4</li>
</ul>
</div>
and JQUERY
$(function () {
var url = window.location.pathname,
urlRegExp = new RegExp(url.replace(/\/$/, '') + "$");
$('#top a').each(function () {
if (urlRegExp.test(this.href.replace(/\/$/, ''))) {
$(this).addClass('active');
}
});
});
The problem is that when i click on the Home link all tabs are getting active class and don't understand why. I need it for the first link to not get any active class.
I'm also looking for this solution and I have tested your code. On the first approach all links are highlighted and when I click other links it is working properly. The problem was on the home page because all links are highlighted because there is "no event been received" when the page is loaded, the code will work if you send a command or by clicking each links, theoretically. To stop this behavior, I found this code to one of the answers above, add this code and change the ".sibling()" to ".previousSibling()"
$(this).parent().sibling().find('a').removeClass('active');
".sibling()" will highlighted at the end of your links change it to ".previousSibling()" so it will go to first (Li)
$(this).parent().previoussibling().find('a').removeClass('active');
Your code will be like this:
$(function () {
var url = window.location.pathname,
urlRegExp = new RegExp(url.replace(/\/$/, '') + "$");
$('#top a').each(function () {
if (urlRegExp.test(this.href.replace(/\/$/, ''))) {
$(this).addClass('active');
$(this).parent().previoussibling().find('a').removeClass('active');
}
});
});
Check this , this will only activates clicked tab , remove active for all and then add for the one clicked
$("#top a").click(function() {
$('a').removeClass('active');
$(this).addClass("active");
});
Check this
You may try this out. It will help you:
<script type="text/javascript">
$(function () {
$('#sidebar li a').each(function () {
var path = window.location.pathname;
if (path.indexOf('?') > 0) {
var current = path.indexOf('?');
}
else {
var current = path;
}
var url = $(this).attr('href');
var currenturl = url.substring(url.lastIndexOf('.') + 1);
if (currenturl.toLowerCase() == current.toLowerCase()) {
$(this).addClass('active');
var par = $(this).parent();
par.addClass('open');
}
});
});
</script>
You're running a foreach loop on all <a> tags within your #top div. So of course it'll add the class active to all of them.
I think what you're trying to do is this: http://jsfiddle.net/rLddf/4/
I used the click event instead.
edit switched link to Kristof Feys example - more efficient.
try this...
$(function() {
$('#yourMenu a').each(function(){
$current = location.href;
$target= $(this).attr('href');
if ( $target== $current)
{
$(this).addClass('active');
}
});
});
I came across the same issue and I found it easier to just add an additional if statement so that the function would fire on all pages except the home page which met my needs.
$(function(){
var url = window.location.pathname,
urlRegExp = new RegExp(url.replace(/\/$/,'') + "$");
$('#top a').each(function(){
if ( window.location.pathname != '/' ){
if(urlRegExp.test(this.href.replace(/\/$/,''))){
$(this).addClass('active');
}
}
});
});
You could also add in an else statement to show an active link on the homepage by targeting the link directly if required.
I think you need something like this:
$(function () {
$("#top a").click(function(e){
e.preventDefault();
$(this).addClass('active');
$(this).parent().siblings().find('a').removeClass('active');
});
});
Fiddle Demo

jQuery click() still activates anchor, even though click function returns false

I've got a very basic jQuery tab setup. Everything worked fine, but I needed a hash in the URL to dictate the active tab, so I added a condition to check for the hash. Now when the page loads, it's actually activating the anchor and shifting the page down. Why isn't the "return false;" working?
$(document).ready( function() {
$(".tabs a").click(function() {
$(".tabs a").removeClass("active");
$(".tabs a").addClass("button secondary");
$(this).attr("class","button active");
var href = $(this).attr("href");
$(href).parent().find("> .active").removeClass("active");
$(href).addClass("active");
return false;
});
if(window.location.hash) {
$(".tabs a[href$='"+window.location.hash+"']").click();
} else {
$(".tabs a:eq(0)").click(); //default to first tab
}
});
**
Here are some updates:
**
If I simply enter the actual hash value instead of pull it from window.location.hash, it works perfectly.
$(".tabs a[href$='#Contact2']").click();
Clicking the different tabs DOES NOT shift the page, only when the page loads and automatically clicks the tab based on the hash value.
If I place a conditional and then automatically click without using a a variable within the jquery selector, it works fine, assuming the location hash does not match the hash I'm clicking (strange, I know...)
if(window.location.hash === "#Contact2") {
$(".tabs a[href$='#Contact4']").triggerHandler("click");
} else {
$(".tabs a:eq(0)").click(); //default to first tab
}
This really doesn't make much sense to me. It seems that the only issue is using the window.location.hash within the jquery selector...
Maybe also this code should make your work:
$(".tabs a").click(function(){
// your code
return false;
});
The other way is:
$(".tabs a").click(function(e) {
e.preventDefault();
// your code
});
Your approach is very recursive. Try to avoid running a collection across a collection.
Here are a few snippets from a jQuery 1.7+ implementation that might do what you need.
... initialization
$(body).on("click", ".tabs a", { }, _selectTab);
... some function
_selectTab : function(event) {
$(".tabs a").removeClass(selected); // clear all matches.
$(event.currentTarget).addClass(selected); // something like that.
}
Try preventDefault() instead. Don't forget the e inside .click(function(e).
$(document).ready( function() {
$(".tabs a").each(function() {
$(this).click(function(e) {
e.preventDefault();
$(".tabs a").removeClass("button secondary active");
$(".tabs a").addClass("button secondary");
$(this).attr("class","button active");
var href = $(this).attr("href");
$(href).parent().find("> .active").removeClass("active");
$(href).addClass("active");
});
});
if(window.location.hash) {
$(".tabs a[href$='"+window.location.hash+"']").click();
} else {
$(".tabs a:eq(0)").click(); //default to first tab
}
});
And then chaining some things together...
$(document).ready( function() {
$(".tabs a").each(function() {
$(this).click(function(e) {
e.preventDefault();
$(".tabs a").removeClass("button secondary active").addClass("button secondary");
$(this).attr("class","button active");
var href = $(this).attr("href");
$(href).parent().find("> .active").removeClass("active")
$(href).addClass("active");
});
});
if(window.location.hash) {
$(".tabs a[href$='"+window.location.hash+"']").click();
} else {
$(".tabs a:eq(0)").click(); //default to first tab
}
});
I'm not sure what you're doing here...
$(".tabs a").removeClass("button secondary active")
$(".tabs a").addClass("button secondary");
The second line instantly adds two classes you removed with the first line.
Instead of .click() use .triggerHandler("click")
Also, your click handler needs an e event parameter and you need to call e.preventDefault() instead of the old-school return false.
Lastly, instead of if(window.location.hash) use if(window.location.hash.length > 0)
Pure Javascript Solution to stop all hash default click
var a_tag = document.getElementsByTagName('a');
for (var i = 0; i < a_tag.length; i++) {
a_tag[i].addEventListener('click', function (e) {
var lastChar = this.href.substr(this.href.length - 1);
if (lastChar == "#") {
e.preventDefault();
}
}, false);
}

How do I prevent the browser from jumping to an anchor when the URL's fragment identifier is changed to it? [duplicate]

We've got a few pages using ajax to load in content and there's a few occasions where we need to deep link into a page. Instead of having a link to "Users" and telling people to click "settings" it's helpful to be able to link people to user.aspx#settings
To allow people to provide us with correct links to sections (for tech support, etc.) I've got it set up to automatically modify the hash in the URL whenever a button is clicked. The only issue of course is that when this happens, it also scrolls the page to this element.
Is there a way to disable this? Below is how I'm doing this so far.
$(function(){
//This emulates a click on the correct button on page load
if(document.location.hash){
$("#buttons li a").removeClass('selected');
s=$(document.location.hash).addClass('selected').attr("href").replace("javascript:","");
eval(s);
}
//Click a button to change the hash
$("#buttons li a").click(function(){
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash=$(this).attr("id")
//return false;
});
});
I had hoped the return false; would stop the page from scrolling - but it just makes the link not work at all. So that's just commented out for now so I can navigate.
Any ideas?
Use history.replaceState or history.pushState* to change the hash. This will not trigger the jump to the associated element.
Example
$(document).on('click', 'a[href^=#]', function(event) {
event.preventDefault();
history.pushState({}, '', this.href);
});
Demo on JSFiddle
* If you want history forward and backward support
History behaviour
If you are using history.pushState and you don't want page scrolling when the user uses the history buttons of the browser (forward/backward) check out the experimental scrollRestoration setting (Chrome 46+ only).
history.scrollRestoration = 'manual';
spec
info
Browser Support
replaceState
pushState
polyfill
Step 1: You need to defuse the node ID, until the hash has been set. This is done by removing the ID off the node while the hash is being set, and then adding it back on.
hash = hash.replace( /^#/, '' );
var node = $( '#' + hash );
if ( node.length ) {
node.attr( 'id', '' );
}
document.location.hash = hash;
if ( node.length ) {
node.attr( 'id', hash );
}
Step 2: Some browsers will trigger the scroll based on where the ID'd node was last seen so you need to help them a little. You need to add an extra div to the top of the viewport, set its ID to the hash, and then roll everything back:
hash = hash.replace( /^#/, '' );
var fx, node = $( '#' + hash );
if ( node.length ) {
node.attr( 'id', '' );
fx = $( '<div></div>' )
.css({
position:'absolute',
visibility:'hidden',
top: $(document).scrollTop() + 'px'
})
.attr( 'id', hash )
.appendTo( document.body );
}
document.location.hash = hash;
if ( node.length ) {
fx.remove();
node.attr( 'id', hash );
}
Step 3: Wrap it in a plugin and use that instead of writing to location.hash...
I think I may have found a fairly simple solution. The problem is that the hash in the URL is also an element on the page that you get scrolled to. if I just prepend some text to the hash, now it no longer references an existing element!
$(function(){
//This emulates a click on the correct button on page load
if(document.location.hash){
$("#buttons li a").removeClass('selected');
s=$(document.location.hash.replace("btn_","")).addClass('selected').attr("href").replace("javascript:","");
eval(s);
}
//Click a button to change the hash
$("#buttons li a").click(function(){
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash="btn_"+$(this).attr("id")
//return false;
});
});
Now the URL appears as page.aspx#btn_elementID which is not a real ID on the page. I just remove "btn_" and get the actual element ID
I was recently building a carousel which relies on window.location.hash to maintain state and made the discovery that Chrome and webkit browsers will force scrolling (even to a non visible target) with an awkward jerk when the window.onhashchange event is fired.
Even attempting to register a handler which stops propogation:
$(window).on("hashchange", function(e) {
e.stopPropogation();
e.preventDefault();
});
Did nothing to stop the default browser behavior.
The solution I found was using window.history.pushState to change the hash without triggering the undesirable side-effects.
$("#buttons li a").click(function(){
var $self, id, oldUrl;
$self = $(this);
id = $self.attr('id');
$self.siblings().removeClass('selected'); // Don't re-query the DOM!
$self.addClass('selected');
if (window.history.pushState) {
oldUrl = window.location.toString();
// Update the address bar
window.history.pushState({}, '', '#' + id);
// Trigger a custom event which mimics hashchange
$(window).trigger('my.hashchange', [window.location.toString(), oldUrl]);
} else {
// Fallback for the poors browsers which do not have pushState
window.location.hash = id;
}
// prevents the default action of clicking on a link.
return false;
});
You can then listen for both the normal hashchange event and my.hashchange:
$(window).on('hashchange my.hashchange', function(e, newUrl, oldUrl){
// #todo - do something awesome!
});
A snippet of your original code:
$("#buttons li a").click(function(){
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash=$(this).attr("id")
});
Change this to:
$("#buttons li a").click(function(e){
// need to pass in "e", which is the actual click event
e.preventDefault();
// the preventDefault() function ... prevents the default action.
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash=$(this).attr("id")
});
Okay, this is a rather old topic but I thought I'd chip in as the 'correct' answer doesn't work well with CSS.
This solution basically prevents the click event from moving the page so we can get the scroll position first. Then we manually add the hash and the browser automatically triggers a hashchange event. We capture the hashchange event and scroll back to the correct position. A callback separates and prevents your code causing a delay by keeping your hash hacking in one place.
var hashThis = function( $elem, callback ){
var scrollLocation;
$( $elem ).on( "click", function( event ){
event.preventDefault();
scrollLocation = $( window ).scrollTop();
window.location.hash = $( event.target ).attr('href').substr(1);
});
$( window ).on( "hashchange", function( event ){
$( window ).scrollTop( scrollLocation );
if( typeof callback === "function" ){
callback();
}
});
}
hashThis( $( ".myAnchor" ), function(){
// do something useful!
});
Adding this here because the more relevant questions have all been marked as duplicates pointing here…
My situation is simpler:
user clicks the link (a[href='#something'])
click handler does: e.preventDefault()
smoothscroll function: $("html,body").stop(true,true).animate({ "scrollTop": linkoffset.top }, scrollspeed, "swing" );
then window.location = link;
This way, the scroll occurs, and there's no jump when the location is updated.
Erm I have a somewhat crude but definitely working method.
Just store the current scroll position in a temp variable and then reset it after changing the hash. :)
So for the original example:
$("#buttons li a").click(function(){
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
var scrollPos = $(document).scrollTop();
document.location.hash=$(this).attr("id")
$(document).scrollTop(scrollPos);
});
I don't think this is possible. As far as I know, the only time a browser doesn't scroll to a changed document.location.hash is if the hash doesn't exist within the page.
This article isn't directly related to your question, but it discusses typical browser behavior of changing document.location.hash
if you use hashchange event with hash parser, you can prevent default action on links and change location.hash adding one character to have difference with id property of an element
$('a[href^=#]').on('click', function(e){
e.preventDefault();
location.hash = $(this).attr('href')+'/';
});
$(window).on('hashchange', function(){
var a = /^#?chapter(\d+)-section(\d+)\/?$/i.exec(location.hash);
});
Save scroll position before changing url fragment.
Change url fragment.
Restore old scroll position.
let oldScrollPosition = window.scrollY;
window.location.hash = addressFragment;
window.scrollTo(0, oldScrollPosition);
It's fast, so client won't notice anything.
The other way to do this is to add a div that's hidden at the top of the viewport. This div is then assigned the id of the hash before the hash is added to the url....so then you don't get a scroll.
Here's my solution for history-enabled tabs:
var tabContainer = $(".tabs"),
tabsContent = tabContainer.find(".tabsection").hide(),
tabNav = $(".tab-nav"), tabs = tabNav.find("a").on("click", function (e) {
e.preventDefault();
var href = this.href.split("#")[1]; //mydiv
var target = "#" + href; //#myDiv
tabs.each(function() {
$(this)[0].className = ""; //reset class names
});
tabsContent.hide();
$(this).addClass("active");
var $target = $(target).show();
if ($target.length === 0) {
console.log("Could not find associated tab content for " + target);
}
$target.removeAttr("id");
// TODO: You could add smooth scroll to element
document.location.hash = target;
$target.attr("id", href);
return false;
});
And to show the last-selected tab:
var currentHashURL = document.location.hash;
if (currentHashURL != "") { //a tab was set in hash earlier
// show selected
$(currentHashURL).show();
}
else { //default to show first tab
tabsContent.first().show();
}
// Now set the tab to active
tabs.filter("[href*='" + currentHashURL + "']").addClass("active");
Note the *= on the filter call. This is a jQuery-specific thing, and without it, your history-enabled tabs will fail.
This solution creates a div at the actual scrollTop and removes it after changing hash:
$('#menu a').on('click',function(){
//your anchor event here
var href = $(this).attr('href');
window.location.hash = href;
if(window.location.hash == href)return false;
var $jumpTo = $('body').find(href);
$('body').append(
$('<div>')
.attr('id',$jumpTo.attr('id'))
.addClass('fakeDivForHash')
.data('realElementForHash',$jumpTo.removeAttr('id'))
.css({'position':'absolute','top':$(window).scrollTop()})
);
window.location.hash = href;
});
$(window).on('hashchange', function(){
var $fakeDiv = $('.fakeDivForHash');
if(!$fakeDiv.length)return true;
$fakeDiv.data('realElementForHash').attr('id',$fakeDiv.attr('id'));
$fakeDiv.remove();
});
optional, triggering anchor event at page load:
$('#menu a[href='+window.location.hash+']').click();
I have a simpler method that works for me. Basically, remember what the hash actually is in HTML. It's an anchor link to a Name tag. That's why it scrolls...the browser is attempting to scroll to an anchor link. So, give it one!
Right under the BODY tag, put your version of this:
<a name="home"></a><a name="firstsection"></a><a name="secondsection"></a><a name="thirdsection"></a>
Name your section divs with classes instead of IDs.
In your processing code, strip off the hash mark and replace with a dot:
var trimPanel = loadhash.substring(1); //lose the hash
var dotSelect = '.' + trimPanel; //replace hash with dot
$(dotSelect).addClass("activepanel").show(); //show the div associated with the hash.
Finally, remove element.preventDefault or return: false and allow the nav to happen. The window will stay at the top, the hash will be appended to the address bar url, and the correct panel will open.
I think you need to reset scroll to its position before hashchange.
$(function(){
//This emulates a click on the correct button on page load
if(document.location.hash) {
$("#buttons li a").removeClass('selected');
s=$(document.location.hash).addClass('selected').attr("href").replace("javascript:","");
eval(s);
}
//Click a button to change the hash
$("#buttons li a").click(function() {
var scrollLocation = $(window).scrollTop();
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash = $(this).attr("id");
$(window).scrollTop( scrollLocation );
});
});
If on your page you use id as sort of an anchor point, and you have scenarios where you want to have users to append #something to the end of the url and have the page scroll to that #something section by using your own defined animated javascript function, hashchange event listener will not be able to do that.
If you simply put a debugger immediate after hashchange event, for example, something like this(well, I use jquery, but you get the point):
$(window).on('hashchange', function(){debugger});
You will notice that as soon as you change your url and hit the enter button, the page stops at the corresponding section immediately, only after that, your own defined scrolling function will get triggered, and it sort of scrolls to that section, which looks very bad.
My suggestion is:
do not use id as your anchor point to the section you want to scroll to.
If you must use ID, like I do. Use 'popstate' event listener instead, it will not automatically scroll to the very section you append to the url, instead, you can call your own defined function inside the popstate event.
$(window).on('popstate', function(){myscrollfunction()});
Finally you need to do a bit trick in your own defined scrolling function:
let hash = window.location.hash.replace(/^#/, '');
let node = $('#' + hash);
if (node.length) {
node.attr('id', '');
}
if (node.length) {
node.attr('id', hash);
}
delete id on your tag and reset it.
This should do the trick.
This worked for me using replaceState:
$('a[href^="#"]').click(function(){
history.replaceState({}, '', location.toString().replace(/#.*$/, '') + $(this).attr('href'));
});
Only add this code into jQuery on document ready
Ref : http://css-tricks.com/snippets/jquery/smooth-scrolling/
$(function() {
$('a[href*=#]:not([href=#])').click(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top
}, 1000);
return false;
}
}
});
});

Modifying location.hash without page scrolling

We've got a few pages using ajax to load in content and there's a few occasions where we need to deep link into a page. Instead of having a link to "Users" and telling people to click "settings" it's helpful to be able to link people to user.aspx#settings
To allow people to provide us with correct links to sections (for tech support, etc.) I've got it set up to automatically modify the hash in the URL whenever a button is clicked. The only issue of course is that when this happens, it also scrolls the page to this element.
Is there a way to disable this? Below is how I'm doing this so far.
$(function(){
//This emulates a click on the correct button on page load
if(document.location.hash){
$("#buttons li a").removeClass('selected');
s=$(document.location.hash).addClass('selected').attr("href").replace("javascript:","");
eval(s);
}
//Click a button to change the hash
$("#buttons li a").click(function(){
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash=$(this).attr("id")
//return false;
});
});
I had hoped the return false; would stop the page from scrolling - but it just makes the link not work at all. So that's just commented out for now so I can navigate.
Any ideas?
Use history.replaceState or history.pushState* to change the hash. This will not trigger the jump to the associated element.
Example
$(document).on('click', 'a[href^=#]', function(event) {
event.preventDefault();
history.pushState({}, '', this.href);
});
Demo on JSFiddle
* If you want history forward and backward support
History behaviour
If you are using history.pushState and you don't want page scrolling when the user uses the history buttons of the browser (forward/backward) check out the experimental scrollRestoration setting (Chrome 46+ only).
history.scrollRestoration = 'manual';
spec
info
Browser Support
replaceState
pushState
polyfill
Step 1: You need to defuse the node ID, until the hash has been set. This is done by removing the ID off the node while the hash is being set, and then adding it back on.
hash = hash.replace( /^#/, '' );
var node = $( '#' + hash );
if ( node.length ) {
node.attr( 'id', '' );
}
document.location.hash = hash;
if ( node.length ) {
node.attr( 'id', hash );
}
Step 2: Some browsers will trigger the scroll based on where the ID'd node was last seen so you need to help them a little. You need to add an extra div to the top of the viewport, set its ID to the hash, and then roll everything back:
hash = hash.replace( /^#/, '' );
var fx, node = $( '#' + hash );
if ( node.length ) {
node.attr( 'id', '' );
fx = $( '<div></div>' )
.css({
position:'absolute',
visibility:'hidden',
top: $(document).scrollTop() + 'px'
})
.attr( 'id', hash )
.appendTo( document.body );
}
document.location.hash = hash;
if ( node.length ) {
fx.remove();
node.attr( 'id', hash );
}
Step 3: Wrap it in a plugin and use that instead of writing to location.hash...
I think I may have found a fairly simple solution. The problem is that the hash in the URL is also an element on the page that you get scrolled to. if I just prepend some text to the hash, now it no longer references an existing element!
$(function(){
//This emulates a click on the correct button on page load
if(document.location.hash){
$("#buttons li a").removeClass('selected');
s=$(document.location.hash.replace("btn_","")).addClass('selected').attr("href").replace("javascript:","");
eval(s);
}
//Click a button to change the hash
$("#buttons li a").click(function(){
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash="btn_"+$(this).attr("id")
//return false;
});
});
Now the URL appears as page.aspx#btn_elementID which is not a real ID on the page. I just remove "btn_" and get the actual element ID
I was recently building a carousel which relies on window.location.hash to maintain state and made the discovery that Chrome and webkit browsers will force scrolling (even to a non visible target) with an awkward jerk when the window.onhashchange event is fired.
Even attempting to register a handler which stops propogation:
$(window).on("hashchange", function(e) {
e.stopPropogation();
e.preventDefault();
});
Did nothing to stop the default browser behavior.
The solution I found was using window.history.pushState to change the hash without triggering the undesirable side-effects.
$("#buttons li a").click(function(){
var $self, id, oldUrl;
$self = $(this);
id = $self.attr('id');
$self.siblings().removeClass('selected'); // Don't re-query the DOM!
$self.addClass('selected');
if (window.history.pushState) {
oldUrl = window.location.toString();
// Update the address bar
window.history.pushState({}, '', '#' + id);
// Trigger a custom event which mimics hashchange
$(window).trigger('my.hashchange', [window.location.toString(), oldUrl]);
} else {
// Fallback for the poors browsers which do not have pushState
window.location.hash = id;
}
// prevents the default action of clicking on a link.
return false;
});
You can then listen for both the normal hashchange event and my.hashchange:
$(window).on('hashchange my.hashchange', function(e, newUrl, oldUrl){
// #todo - do something awesome!
});
A snippet of your original code:
$("#buttons li a").click(function(){
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash=$(this).attr("id")
});
Change this to:
$("#buttons li a").click(function(e){
// need to pass in "e", which is the actual click event
e.preventDefault();
// the preventDefault() function ... prevents the default action.
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash=$(this).attr("id")
});
Okay, this is a rather old topic but I thought I'd chip in as the 'correct' answer doesn't work well with CSS.
This solution basically prevents the click event from moving the page so we can get the scroll position first. Then we manually add the hash and the browser automatically triggers a hashchange event. We capture the hashchange event and scroll back to the correct position. A callback separates and prevents your code causing a delay by keeping your hash hacking in one place.
var hashThis = function( $elem, callback ){
var scrollLocation;
$( $elem ).on( "click", function( event ){
event.preventDefault();
scrollLocation = $( window ).scrollTop();
window.location.hash = $( event.target ).attr('href').substr(1);
});
$( window ).on( "hashchange", function( event ){
$( window ).scrollTop( scrollLocation );
if( typeof callback === "function" ){
callback();
}
});
}
hashThis( $( ".myAnchor" ), function(){
// do something useful!
});
Adding this here because the more relevant questions have all been marked as duplicates pointing here…
My situation is simpler:
user clicks the link (a[href='#something'])
click handler does: e.preventDefault()
smoothscroll function: $("html,body").stop(true,true).animate({ "scrollTop": linkoffset.top }, scrollspeed, "swing" );
then window.location = link;
This way, the scroll occurs, and there's no jump when the location is updated.
Erm I have a somewhat crude but definitely working method.
Just store the current scroll position in a temp variable and then reset it after changing the hash. :)
So for the original example:
$("#buttons li a").click(function(){
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
var scrollPos = $(document).scrollTop();
document.location.hash=$(this).attr("id")
$(document).scrollTop(scrollPos);
});
I don't think this is possible. As far as I know, the only time a browser doesn't scroll to a changed document.location.hash is if the hash doesn't exist within the page.
This article isn't directly related to your question, but it discusses typical browser behavior of changing document.location.hash
if you use hashchange event with hash parser, you can prevent default action on links and change location.hash adding one character to have difference with id property of an element
$('a[href^=#]').on('click', function(e){
e.preventDefault();
location.hash = $(this).attr('href')+'/';
});
$(window).on('hashchange', function(){
var a = /^#?chapter(\d+)-section(\d+)\/?$/i.exec(location.hash);
});
Save scroll position before changing url fragment.
Change url fragment.
Restore old scroll position.
let oldScrollPosition = window.scrollY;
window.location.hash = addressFragment;
window.scrollTo(0, oldScrollPosition);
It's fast, so client won't notice anything.
The other way to do this is to add a div that's hidden at the top of the viewport. This div is then assigned the id of the hash before the hash is added to the url....so then you don't get a scroll.
Here's my solution for history-enabled tabs:
var tabContainer = $(".tabs"),
tabsContent = tabContainer.find(".tabsection").hide(),
tabNav = $(".tab-nav"), tabs = tabNav.find("a").on("click", function (e) {
e.preventDefault();
var href = this.href.split("#")[1]; //mydiv
var target = "#" + href; //#myDiv
tabs.each(function() {
$(this)[0].className = ""; //reset class names
});
tabsContent.hide();
$(this).addClass("active");
var $target = $(target).show();
if ($target.length === 0) {
console.log("Could not find associated tab content for " + target);
}
$target.removeAttr("id");
// TODO: You could add smooth scroll to element
document.location.hash = target;
$target.attr("id", href);
return false;
});
And to show the last-selected tab:
var currentHashURL = document.location.hash;
if (currentHashURL != "") { //a tab was set in hash earlier
// show selected
$(currentHashURL).show();
}
else { //default to show first tab
tabsContent.first().show();
}
// Now set the tab to active
tabs.filter("[href*='" + currentHashURL + "']").addClass("active");
Note the *= on the filter call. This is a jQuery-specific thing, and without it, your history-enabled tabs will fail.
This solution creates a div at the actual scrollTop and removes it after changing hash:
$('#menu a').on('click',function(){
//your anchor event here
var href = $(this).attr('href');
window.location.hash = href;
if(window.location.hash == href)return false;
var $jumpTo = $('body').find(href);
$('body').append(
$('<div>')
.attr('id',$jumpTo.attr('id'))
.addClass('fakeDivForHash')
.data('realElementForHash',$jumpTo.removeAttr('id'))
.css({'position':'absolute','top':$(window).scrollTop()})
);
window.location.hash = href;
});
$(window).on('hashchange', function(){
var $fakeDiv = $('.fakeDivForHash');
if(!$fakeDiv.length)return true;
$fakeDiv.data('realElementForHash').attr('id',$fakeDiv.attr('id'));
$fakeDiv.remove();
});
optional, triggering anchor event at page load:
$('#menu a[href='+window.location.hash+']').click();
I have a simpler method that works for me. Basically, remember what the hash actually is in HTML. It's an anchor link to a Name tag. That's why it scrolls...the browser is attempting to scroll to an anchor link. So, give it one!
Right under the BODY tag, put your version of this:
<a name="home"></a><a name="firstsection"></a><a name="secondsection"></a><a name="thirdsection"></a>
Name your section divs with classes instead of IDs.
In your processing code, strip off the hash mark and replace with a dot:
var trimPanel = loadhash.substring(1); //lose the hash
var dotSelect = '.' + trimPanel; //replace hash with dot
$(dotSelect).addClass("activepanel").show(); //show the div associated with the hash.
Finally, remove element.preventDefault or return: false and allow the nav to happen. The window will stay at the top, the hash will be appended to the address bar url, and the correct panel will open.
I think you need to reset scroll to its position before hashchange.
$(function(){
//This emulates a click on the correct button on page load
if(document.location.hash) {
$("#buttons li a").removeClass('selected');
s=$(document.location.hash).addClass('selected').attr("href").replace("javascript:","");
eval(s);
}
//Click a button to change the hash
$("#buttons li a").click(function() {
var scrollLocation = $(window).scrollTop();
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash = $(this).attr("id");
$(window).scrollTop( scrollLocation );
});
});
If on your page you use id as sort of an anchor point, and you have scenarios where you want to have users to append #something to the end of the url and have the page scroll to that #something section by using your own defined animated javascript function, hashchange event listener will not be able to do that.
If you simply put a debugger immediate after hashchange event, for example, something like this(well, I use jquery, but you get the point):
$(window).on('hashchange', function(){debugger});
You will notice that as soon as you change your url and hit the enter button, the page stops at the corresponding section immediately, only after that, your own defined scrolling function will get triggered, and it sort of scrolls to that section, which looks very bad.
My suggestion is:
do not use id as your anchor point to the section you want to scroll to.
If you must use ID, like I do. Use 'popstate' event listener instead, it will not automatically scroll to the very section you append to the url, instead, you can call your own defined function inside the popstate event.
$(window).on('popstate', function(){myscrollfunction()});
Finally you need to do a bit trick in your own defined scrolling function:
let hash = window.location.hash.replace(/^#/, '');
let node = $('#' + hash);
if (node.length) {
node.attr('id', '');
}
if (node.length) {
node.attr('id', hash);
}
delete id on your tag and reset it.
This should do the trick.
This worked for me using replaceState:
$('a[href^="#"]').click(function(){
history.replaceState({}, '', location.toString().replace(/#.*$/, '') + $(this).attr('href'));
});
Only add this code into jQuery on document ready
Ref : http://css-tricks.com/snippets/jquery/smooth-scrolling/
$(function() {
$('a[href*=#]:not([href=#])').click(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top
}, 1000);
return false;
}
}
});
});

Categories