修改 location.hash 而不滚动页面

我们有一些页面使用 ajax 加载内容,有些情况下我们需要深度链接到一个页面。与其链接到“用户”,告诉人们点击“设置”,不如链接到 Aspx # 设置

为了让人们提供正确的链接给我们的部分(为技术支持等) ,我已经设置了它自动修改的散列在网址时,一个按钮被点击。当然,唯一的问题是,当这种情况发生时,它还会将页面滚动到这个元素。

有没有办法禁用这个? 下面是我如何做到这一点到目前为止。

$(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;
});
});

我曾希望 return false;会停止页面滚动-但它只是使链接不工作在所有。所以现在只是注释掉了,这样我就可以导航了。

有什么想法吗?

126519 次浏览

我觉得这不可能。据我所知,浏览器唯一不滚动到更改后的 document.location.hash的情况是页面中不存在散列。

本文 与您的问题没有直接关系,但它讨论了更改 document.location.hash的典型浏览器行为

步骤1: 您需要拆除节点 ID,直到设置了散列。这是通过在设置哈希时从节点中删除 ID,然后将其重新添加到。

hash = hash.replace( /^#/, '' );
var node = $( '#' + hash );
if ( node.length ) {
node.attr( 'id', '' );
}
document.location.hash = hash;
if ( node.length ) {
node.attr( 'id', hash );
}

步骤2: 有些浏览器会根据 ID’d 节点最后出现的位置触发滚动,所以您需要稍微帮助一下他们。您需要在 viewport 顶部添加一个额外的 div,将其 ID 设置为散列,然后回滚所有内容:

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 );
}

步骤3: 将它包装在一个插件中,并使用该插件代替写入 location.hash..。

我想我可能找到了一个相当简单的解决办法。问题在于 URL 中的散列也是滚动到的页面上的一个元素。如果我只是在散列表前面添加一些文本,现在它不再引用现有的元素了!

$(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;
});
});

现在 URL 显示为 page.aspx#btn_elementID,这在页面上不是一个真正的 ID。我只需删除“ btn _”并获得实际的元素 ID

原始代码的一个片段:

$("#buttons li a").click(function(){
$("#buttons li a").removeClass('selected');
$(this).addClass('selected');
document.location.hash=$(this).attr("id")
});

将其更改为:

$("#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")
});

另一种方法是添加一个隐藏在视图顶部的 div。然后,在将散列添加到 url 之前,为这个 div 分配散列的 id... ... 这样就不会出现滚动。

使用 history.replaceStatehistory.pushState * 更改散列。这不会触发跳转到关联元素。

例子

$(document).on('click', 'a[href^=#]', function(event) {
event.preventDefault();
history.pushState({}, '', this.href);
});

JSFiddle 演示

如果你希望历史向前和向后支持

历史行为

如果您正在使用 history.pushState,并且当用户使用浏览器的历史记录按钮(向前/向后)时,您不希望页面滚动,请查看实验性的 scrollRestoration设置 (仅支持 Chrome46 +)

history.scrollRestoration = 'manual';

浏览器支持

如果将 hashchange 事件与 hash 解析器一起使用,则可以防止对链接执行默认操作,并更改 location.hash,添加一个字符使其与元素的 id 属性有所不同

$('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);
});

好吧,这是一个相当老的话题,但我认为我应该插一句,因为“正确”的答案不适合 CSS。

这个解决方案基本上是 防止点击事件从移动页面开始,这样我们就可以先得到滚动位置。然后我们手动添加散列,浏览器自动触发 零钱事件。我们捕获 hashchange 事件并滚动回正确的位置。回调通过将散列修改保持在一个位置来分离并防止代码导致延迟。

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!
});

以下是我对启用历史记录标签的解决方案:

    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;
});

并显示最后选择的选项卡:

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");

请注意 filter调用中的 *=。这是一个特定于 jQuery 的东西,如果没有它,启用历史记录的选项卡就会失败。

这个解决方案在实际的 scrollTop 上创建一个 div,并在更改 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();
});

可选的,在页面加载时触发锚事件:

$('#menu a[href='+window.location.hash+']').click();

在这里添加这个,因为更相关的问题都被标记为重复的指向这里..。

我的情况要简单得多:

  • 用户单击链接(a[href='#something'])
  • 单击处理程序: e.preventDefault()
  • 函数: $("html,body").stop(true,true).animate({ "scrollTop": linkoffset.top }, scrollspeed, "swing" );
  • 然后 window.location = link;

这样,滚动就发生了,并且在更新位置时不会发生跳转。

嗯,我有一个有点粗糙,但肯定的工作方法。
只需将当前滚动位置存储在一个临时变量中,然后在更改 hash. : 后重置该变量即可

因此,对于最初的例子:

$("#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);
});

在文档准备就绪时,只需将此代码添加到 jQuery 中

档号: 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;
}
}
});
});

我最近正在构建一个旋转木马,它依赖于 window.location.hash来维护状态,并且发现当 window.onhashchange事件被触发时,Chrome 和 webkit 浏览器会强制滚动(即使是对一个不可见的目标)。

甚至尝试注册一个处理程序来停止繁殖:

$(window).on("hashchange", function(e) {
e.stopPropogation();
e.preventDefault();
});

没有停止默认浏览器行为。 我找到的解决方案是使用 window.history.pushState来更改散列,而不会引发不希望的副作用。

 $("#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;
});

然后,您可以同时侦听普通的 hashchange 事件和 my.hashchange:

$(window).on('hashchange my.hashchange', function(e, newUrl, oldUrl){
// @todo - do something awesome!
});

我有个更简单的方法。基本上,要记住 HTML 中的散列实际上是什么。这是一个到 Name 标签的锚链接。这就是为什么它会滚动... 浏览器试图滚动到一个锚链接。所以,给它一个!

  1. 在尸体标签下面,写上你的版本:
 <a name="home"></a><a name="firstsection"></a><a name="secondsection"></a><a name="thirdsection"></a>
  1. Name your section divs with classes instead of IDs.

  2. 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.

最后,删除 element.proventDefault 或 return: false 并允许进行导航。窗口将停留在顶部,散列将被追加到地址栏的网址,正确的面板将打开。

我认为你需要在散列改变之前重置滚动条到它的位置。

$(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 );
});
});

如果在你的页面上你使用 id 作为一个锚点,并且你有一些场景,你想让用户在 URL 的末尾添加 # something,然后通过使用你自己定义的动画 javascript 函数让页面滚动到 # something 部分,那么 hashchange 事件监听器将无法做到这一点。

例如,如果你只是在 hashchange 事件之后立即放置一个调试器,类似下面这样的东西(我使用 jquery,但是你明白了) :

$(window).on('hashchange', function(){debugger});

你会注意到,一旦你改变你的网址,点击回车按钮,页面会立即停在相应的区域,只有在那之后,你自己定义的滚动函数才会被触发,它会滚动到那个区域,看起来非常糟糕。

我的建议是:

  1. 不要使用 id 作为要滚动到的部分的锚点。

  2. 如果你一定要用身份证,像我一样。使用“ popstate”事件监听器,它不会自动滚动到你附加到 url 的那一部分,相反,你可以在 popstate 事件中调用你自己定义的函数。

    $(window).on('popstate', function(){myscrollfunction()});

最后,您需要在自己定义的滚动函数中使用一些技巧:

    let hash = window.location.hash.replace(/^#/, '');
let node = $('#' + hash);
if (node.length) {
node.attr('id', '');
}
if (node.length) {
node.attr('id', hash);
}

删除标签上的标识并重置它。

这个应该能行。

  1. 在更改 URL 片段之前保存滚动位置。
  2. 更改网址片段。
  3. 恢复原来的滚动位置。
let oldScrollPosition = window.scrollY;
window.location.hash = addressFragment;
window.scrollTo(0, oldScrollPosition);

它很快,所以客户不会注意到任何东西。

这对我来说使用 replace State 很有用:

$('a[href^="#"]').click(function(){
history.replaceState({}, '', location.toString().replace(/#.*$/, '') + $(this).attr('href'));
});