在不滚动页面的情况下修改 location.hash

IT技术 javascript jquery fragment-identifier
2021-02-06 03:26:43

我们有几个页面使用 ajax 加载内容,并且在某些情况下我们需要深入链接到一个页面。与其拥有指向“用户”的链接并告诉人们单击“设置”,不如将人们链接到user.aspx#settings

为了让人们向我们提供正确的部分链接(用于技术支持等),我已将其设置为在单击按钮时自动修改 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).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;会阻止页面滚动 - 但它只会使链接根本不起作用。所以现在只是注释掉,所以我可以导航。

有任何想法吗?

6个回答

第 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 节点的位置触发滚动,因此您需要帮助他们一点。您需要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...

了解哪些浏览器需要此处的第 2 步会很有用。
2021-03-14 03:26:43
非常感谢,这也帮助了我。但我也注意到了一个副作用:我经常通过锁定“鼠标中键”并简单地将鼠标移动到我想要滚动的方向来滚动。在谷歌浏览器中,这会中断滚动流。可能有一个奇特的解决方法吗?
2021-03-18 03:26:43
谢谢博尔加。尽管您在删除目标节点的 id 之前附加了 fx div,但让我感到困惑。这意味着文档中存在重复 ID 的时刻。似乎是一个潜在的问题,或者至少是不礼貌的 ;)
2021-03-28 03:26:43
不,步骤 2 中发生的事情是创建了一个隐藏的 div 并将其放置在滚动的当前位置。它在视觉上与 position:fixed/top:0 相同。因此滚动条被“移动”到它当前所在的完全相同的位置。
2021-03-29 03:26:43
这个解决方案确实很有效 - 但是这一行: top: $.scroll().top + 'px' 应该是: top: $(window).scrollTop() + 'px'
2021-04-10 03:26:43

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

例子

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

在 JSFiddle 上演示

* 如果你想要历史向前和向后支持

历史行为

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

history.scrollRestoration = 'manual';

浏览器支持

replaceState可能是去这里更好的方式。不同之处在于pushState在您的历史记录添加了一个项目,而replaceState没有。
2021-03-14 03:26:43
body滚动的并不总是,有时是documentElement请参阅Diego Perini 的这篇要点
2021-03-20 03:26:43
谢谢@AakilFernandes,你是对的。我刚刚更新了答案。
2021-04-01 03:26:43
@user151496 查看:stackoverflow.com/revisions/...
2021-04-04 03:26:43
对 ie8/9 有什么想法吗?
2021-04-08 03:26:43

我想我可能已经找到了一个相当简单的解决方案。问题是 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

喜欢这个解决方案!我们将 URL 哈希用于基于 AJAX 的导航,在 ID 前面加上一个/似乎合乎逻辑的。
2021-03-14 03:26:43
如果您在 CSS 中使用 :target 伪选择器,则不起作用。
2021-03-25 03:26:43
请注意,如果您出于某种原因已经Promise使用 page.aspx#elementID URL,则可以反转此技术并在所有 ID 前添加“btn_”
2021-04-02 03:26:43
很好的解决方案。最无痛的。
2021-04-04 03:26:43

我最近正在构建一个依赖于window.location.hash维护状态的轮播,并发现 Chrome 和 webkit 浏览器会在window.onhashchange触发事件时以尴尬的方式强制滚动(甚至到不可见的目标)

甚至试图注册一个停止传播的处理程序:

$(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!
});
当然my.hashchange只是一个示例事件名称
2021-03-23 03:26:43

原始代码的片段:

$("#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")
});
这将不起作用,因为设置该hash属性将导致页面无论如何滚动。
2021-03-20 03:26:43