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

时间:2009-09-28 22:08:25

标签: javascript jquery fragment-identifier

我们有一些页面使用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;会阻止页面滚动 - 但它只会使链接无法正常工作。所以现在只是注释掉了所以我可以导航。

有什么想法吗?

17 个答案:

答案 0 :(得分:110)

步骤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节点的上次位置触发滚动,因此您需要稍微帮助它们。您需要在视口顶部添加一个额外的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 ...

答案 1 :(得分:88)

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

现在,网址显示为page.aspx#btn_elementID,这不是网页上的真实ID。我只是删除“btn_”并获取实际的元素ID

答案 2 :(得分:83)

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

实施例

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

Demo on JSFiddle

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

历史行为

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

history.scrollRestoration = 'manual';

浏览器支持

答案 3 :(得分:3)

原始代码的片段:

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

答案 4 :(得分:3)

我最近正在构建一个依赖于window.location.hash维护状态的轮播,并发现当{{1} {{1}时,Chrome和webkit浏览器会强制滚动(甚至是不可见的目标)事件被解雇了。

甚至尝试注册停止传播的处理程序:

window.onhashchange

没有什么可以阻止默认的浏览器行为。 我找到的解决方案是使用window.history.pushState来更改哈希,而不会触发不良副作用。

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

然后,您可以监听正常的hashchange事件和 $("#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; });

my.hashchange

答案 5 :(得分:2)

好的,这是一个相当古老的主题,但我认为我会填上因为“正确”的答案与CSS不兼容。

此解决方案基本上阻止点击事件移动页面,以便我们可以先获得滚动位置。然后我们手动添加哈希值,浏览器会自动触发 hashchange 事件。我们捕获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!
});

答案 6 :(得分:2)

嗯,我有一种粗略但绝对有效的方法 只需将当前滚动位置存储在临时变量中,然后在更改散列后重置它。 :)

因此对于原始示例:

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

答案 7 :(得分:1)

我不认为这是可能的。据我所知,浏览器没有滚动到更改document.location.hash的唯一情况是页面中不存在哈希值。

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

答案 8 :(得分:1)

如果你使用带有散列解析器的hashchange事件,你可以阻止对链接的默认操作并更改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);
});

答案 9 :(得分:1)

在此处添加此项,因为更相关的问题都已标记为重复此处...

我的情况比较简单:

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

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

答案 10 :(得分:0)

如果在您的页面上使用id作为一个锚点,并且您有想要让用户将#something附加到网址末尾并让页面滚动到该#something部分的方案,请使用自己定义的动画javascript函数,hashchange事件监听器将无法做到这一点。

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

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

您会注意到,只要您更改了网址并点击了输入按钮,页面就会立即停止在相应的部分,只有在此之后,您自己定义的滚动功能才会被触发,并且它会滚动到该部分,这看起来非常糟糕。

我的建议是:

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

  2. 如果你必须使用ID,就像我一样。使用&#39; popstate&#39;事件监听器,它不会自动滚动到您追加到url的部分,而是可以在popstate事件中调用自己定义的函数。

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

  3. 最后,您需要在自己定义的滚动功能中执行一些技巧:

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

    删除代码上的ID并重置。

    这应该可以解决问题。

答案 11 :(得分:0)

我认为您需要在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 );
    });
});

答案 12 :(得分:0)

我有一个适合我的简单方法。基本上,请记住HTML实际上是什么。它是Name标签的锚链接。这就是它滚动的原因......浏览器正在尝试滚动到锚链接。所以,给它一个!

  1. 在BODY标签下,放置您的版本:
  2.  <a name="home"></a><a name="firstsection"></a><a name="secondsection"></a><a name="thirdsection"></a>
    1. 使用类而不是ID命名您的节div。

    2. 在处理代码中,剥去哈希标记并替换为点:

    3.     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.preventDefault或return:false并允许导航发生。窗口将保持在顶部,哈希将附加到地址栏URL,正确的面板将打开。

答案 13 :(得分:0)

此解决方案在实际的scrollTop中创建一个div,并在更改哈希后将其删除:

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

答案 14 :(得分:0)

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

    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的东西,没有它,你的历史记录选项卡将失败。

答案 15 :(得分:0)

另一种方法是添加一个隐藏在视口顶部的div。然后在将哈希添加到url之前为此div分配哈希的id。这样​​就不会得到滚动。

答案 16 :(得分:-4)

仅在准备好文档的

中将此代码添加到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;
      }
    }
  });
});