修改location.hash时不滚动页面

170
我们有几个页面使用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;可以停止页面滚动,但它只会使链接根本不起作用。所以现在我将其注释掉,以便进行导航。

有什么想法吗?

20个回答

1
如果您使用带哈希解析器的哈希更改事件,您可以防止默认操作并更改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);
});

太棒了!尝试了五个小时来解决哈希重载的问题... :/ 这是唯一有效的方法!!谢谢!!! - Igor Trindade

0

如果您在页面上使用ID作为锚点,并且有一些情况需要用户将#something附加到URL的末尾,并使用自己定义的动画JavaScript函数将页面滚动到该#something部分,则hashchange事件监听器将无法实现。

如果您只是在hashchange事件之后立即放置一个调试器,例如像这样(好吧,我使用jquery,但您理解我的意思):

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

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

我的建议是:

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

  2. 如果你必须使用ID,像我一样。使用'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并重置它。

这应该就可以了。


0
另一种方法是添加一个隐藏在视口顶部的div。然后在将哈希添加到URL之前,将此div分配为哈希的ID...这样你就不会滚动。

0

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

这篇文章虽然与你的问题没有直接关系,但它讨论了更改document.location.hash的典型浏览器行为。


在我的情况下,哈希被添加到URL中,并且哈希存在,但它停止了向下滚动到我想要的部分。不过,使用“Ctrl + L&Enter”刷新可以解决问题...我想知道为什么? - s3c

0

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

  1. 在BODY标签下面,放置您的版本:
 <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);    //去掉哈希
var dotSelect = '.' + trimPanel; //用点替换哈希
$(dotSelect).addClass("activepanel").show(); //显示与哈希相关联的div。

最后,删除element.preventDefault或return:false,并允许导航发生。窗口将保持在顶部,哈希将附加到地址栏URL中,并且正确的面板将打开。


0
这是我对此问题的看法(14年前提问)。我需要:
  • 更新URL而不添加历史记录状态;
  • 阻止默认滚动{{link1:location.replace()}};
  • 在{{link2:history.replaceState()}}不起作用的情况下更新CSS的:target
我的方法是:
  1. 使用location.replace(),但在此之前保存当前滚动位置;
  2. 在窗口{{link3:hashchange}}事件处理程序或文档{{link4:scroll}}事件处理程序中恢复保存的滚动位置(仅一次)- 以先发生的事件为准。
以下示例实现了上述功能,并实现了自定义滚动(可以根据确切的OP目的省略):
<!DOCTYPE html>

<style>
  table { border-collapse: collapse }
  td { border: thin solid;
       min-width: 500px; height: 250px;
       text-align: center;
       vertical-align: middle }
  td:target { outline: medium dashed green;
              outline-offset: 2px }
</style>

<table>
  <tr>
    <td id="1-1">1-1</td>
    <td id="1-2">1-2</td>
    <td id="1-3">1-3</td>
    <td id="1-4">1-4</td>
  </tr>
  <tr>
    <td id="2-1">2-1</td>
    <td id="2-2">2-2</td>
    <td id="2-3">2-3</td>
    <td id="2-4">2-4</td>
  </tr>
  <tr>
    <td id="3-1">3-1</td>
    <td id="3-2">3-2</td>
    <td id="3-3">3-3</td>
    <td id="3-4">3-4</td>
  </tr>
  <tr>
    <td id="4-1">4-1</td>
    <td id="4-2">4-2</td>
    <td id="4-3">4-3</td>
    <td id="4-4">4-4</td>
  </tr>
  <tr>
    <td id="5-1">5-1</td>
    <td id="5-2">5-2</td>
    <td id="5-3">5-3</td>
    <td id="5-4">5-4</td>
  </tr>
</table>

<script>
  (() => {
    let savedScroll;
    let scrollTarget;

    window.addEventListener("hashchange", scrollTargetCellIntoView);
    document.addEventListener("scroll", scrollTargetCellIntoView);
    document.querySelectorAll("td[id]").forEach(cell => {
      cell.onclick = evt => selectTargetCell(evt.target);
    });

    function replaceLocationHash(fragId) {
        // Doesn't update CSS :target
        //history.replaceState({}, "", "#" + cell.id);
        savedScroll = { y: window.scrollY,
                        x: window.scrollX };
        location.replace("#" + fragId);
    }

    function selectTargetCell(cell) {
      scrollTarget = cell;
      replaceLocationHash(cell.id);
    }

    function scrollTargetCellIntoView() {
      if (savedScroll) {
        window.scrollTo({ top: savedScroll.y,
                          left: savedScroll.x,
                          behavior: "instant" });
        savedScroll = null;
      }
      if (scrollTarget) {
        scrollTarget.scrollIntoView({ block: "nearest",
                                      inline: "nearest",
                                      behavior: "smooth" });
        scrollTarget = null;
      }
    }
  })();
</script>

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

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特有的东西,如果没有它,您启用历史记录的选项卡将无法正常工作。

0

我认为你需要在哈希变化之前将滚动条重置到其位置。

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

-5

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接