代码之家  ›  专栏  ›  技术社区  ›  Chris Barr

修改location.hash而不滚动页面

  •  138
  • Chris Barr  · 技术社区  · 15 年前

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

    为了让人们能够向我们提供指向部分的正确链接(用于技术支持等),我已经设置了它,以便在每次单击按钮时自动修改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 回复  |  直到 6 年前
        1
  •  110
  •   Borgar    13 年前

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

    第二步:有些浏览器会根据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
  •  88
  •   Chris Barr    15 年前

    我想我可能找到了一个相当简单的解决办法。问题是,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_uuu”并获取实际元素ID

        3
  •  77
  •   HaNdTriX    10 年前

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

    例子

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

    Demo on JSFiddle

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

    历史行为

    如果您正在使用 历史.pushstate 当用户使用浏览器的历史记录按钮(前进/后退)时,您不希望页面滚动,请查看实验 scrollRestoration 设置 (仅铬46+) .

    history.scrollRestoration = 'manual';
    

    浏览器支持

        4
  •  3
  •   Daniel    15 年前

    原始代码的片段:

    $("#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")
    });
    
        5
  •  3
  •   max Mike Williams    10 年前

    我最近在建造一个旋转木马 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!
    });
    
        6
  •  2
  •   Egor Kloos    11 年前

    好吧,这是一个相当古老的话题,但我想我会插嘴,因为“正确”的答案不适合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!
    });
    
        7
  •  2
  •   Jan Paepke    11 年前

    嗯,我有一个有点粗糙但肯定是工作方法。
    只需将当前滚动位置存储在一个临时变量中,然后在更改哈希值后重置它。:)

    因此,对于原始示例:

    $("#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);
    });
    
        8
  •  1
  •   Ben S    15 年前

    我认为这是不可能的。据我所知,唯一一次浏览器不滚动到已更改的 document.location.hash 如果页中不存在哈希。

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

        9
  •  1
  •   polkila    12 年前

    如果将hash change事件与哈希分析器一起使用,则可以防止对链接执行默认操作并更改位置。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);
    });
    
        10
  •  1
  •   ptim Dmytro Serhatyi    11 年前

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

    我的情况比较简单:

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

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

        11
  •  0
  •   Mike Rifgin    14 年前

    另一种方法是添加隐藏在视区顶部的DIV。然后,在散列被添加到URL之前,这个DIV被分配散列的ID……这样就不会得到滚动。

        12
  •  0
  •   user1429980    11 年前

    以下是我的历史标签解决方案:

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

        13
  •  0
  •   user669677    11 年前

    此解决方案在实际的滚动顶部创建一个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
  •   ColdSharper    9 年前

    我有一个更简单的方法对我有用。基本上,记住哈希在HTML中是什么。它是指向名称标签的锚链接。这就是它滚动的原因……浏览器正试图滚动到锚链接。所以,给它一个!

    1. 在身体标签下面,放上你的版本:
     <a name="home"></a><a name="firstsection"></a><a name="secondsection"></a><a name="thirdsection"></a>
    1. 用类而不是ID命名分区div。

    2. 在处理代码中,去掉散列标记并替换为点:

        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,并打开正确的面板。

        15
  •  0
  •   iProDev    8 年前

    我认为您需要在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 );
        });
    });
    
        16
  •  0
  •   Shuwei    7 年前

    如果在您的页面上,您使用id作为锚定点,并且您有一些场景希望用户在URL的末尾附加一些内容,并使用您自己定义的动画javascript函数将页面滚动到该部分,那么hashchange事件侦听器将无法做到这一点。

    例如,如果您只是在hashchange事件之后立即放置一个调试器,类似于这样(好吧,我使用jquery,但您得到了要点):

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

    您会注意到,一旦您更改了URL并点击了Enter按钮,页面就会立即停止在相应的部分,只有在这之后,您自己定义的滚动函数才会被触发,并且它会滚动到该部分,这看起来非常糟糕。

    我的建议是:

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

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

    这样就可以了。

        17
  •  -4
  •   marc_s    9 年前

    只在文档就绪时将此代码添加到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;
          }
        }
      });
    });