ページをスクロールせずにlocation.hashを変更する


147

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;ページがスクロールしないようにしたいと思っていましたが、リンクがまったく機能しなくなりました。今はコメントアウトされているので、ナビゲートできます。

何か案は?

回答:


111

ステップ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...


1
いいえ、手順2で起こっていることは、非表示のdivが作成され、スクロールの現在の場所に配置されることです。視覚的には、position:fixed / top:0と同じです。したがって、スクロールバーは、現在スクロールバーとまったく同じ場所に「移動」されます。
ボーガー

3
このソリューションは確かにうまく機能しますが、次の行:top:$ .scroll()。top + 'px'でなければなりません:top:$(window).scrollTop()+ 'px'
Mark Perkins

27
ここで手順2が必要なブラウザを知っておくと便利です。
djc

1
ボーガーに感謝します。しかし、ターゲットノードのIDを削除する前にfx divを追加していることに戸惑います。つまり、ドキュメントに重複したIDがある瞬間があるということです。潜在的な問題、または少なくともマナーが悪いようです;)
Ben

1
どうもありがとうございました。これも役に立ちました。しかし、これが動作しているときの副作用にも気付きました。「マウスの中央ボタン」をロックして、スクロールしたい方向にマウスを移動するだけでスクロールすることがよくあります。Google Chromeでは、これによりスクロールフローが中断されます。そのための素晴らしい回避策はありますか?
YMMD 2012

99

history.replaceStateまたはhistory.pushState*を使用してハッシュを変更します。これは、関連する要素へのジャンプをトリガーしません。

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

JSFiddleのデモ

*履歴の前方および後方サポートが必要な場合

歴史的行動

を使用history.pushStateしていて、ユーザーがブラウザーの履歴ボタンを使用したときにページをスクロールしたくない場合(前方/後方)、実験的なscrollRestoration設定(Chrome 46以降のみ)を確認してください。

history.scrollRestoration = 'manual';

ブラウザのサポート


5
replaceStateたぶんここに行くにはもっと良い方法です。違いはpushState履歴にアイテムを追加しますが、追加しreplaceStateません。
Aakil Fernandes、2015

ありがとう@AakilFernandesあなたは正しい。答えを更新しました。
HaNdTriX 2015

bodyスクロールするのは常にであるとは限りませんdocumentElement。Diego Periniによるこの要点
確認

1
ここでie8 / 9に関するアイデアはありますか?
user151496 2015年

1
@ user151496チェックアウト:stackoverflow.com/revisions/...を
HaNdTriX

90

かなり簡単な解決策を見つけたのではないかと思います。問題は、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を取得するだけです


5
素晴らしいソリューション。ロットの中で最も痛みがありません。
Swader

何らかの理由で既にpage.aspx#elementID URLにコミットしている場合は、この手法を逆にして、すべてのIDに「btn_」を付加することができます
joshuahedlund

2
CSSで:target疑似セレクターを使用している場合は機能しません。
ジェイソンTフェザリンガム2012

1
このソリューションが大好きです!AJAXベースのナビゲーションにはURLハッシュを使用しており、IDの前に/論理的なものを付けています。
Connell 2013

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

hashプロパティを設定するとページがスクロールするため、これは機能しません。
jordanbtucker 2014

3

私は最近、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は単なるイベント名の例です
最大

2

さて、これはかなり古いトピックですが、「正しい」答えがCSSでうまく機能しないので、私はチップを入れると思いました。

このソリューションは基本的に、クリックイベントがページを移動するのを防ぎ、最初にスクロール位置を取得できます。次に、手動でハッシュを追加すると、ブラウザーは自動的にhashchangeイベントをトリガーします。hashchangeイベントをキャプチャし、正しい位置にスクロールして戻ります。コールバックは分離し、ハッシュハッキングを1か所に保つことにより、コードが遅延を引き起こすのを防ぎます。

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

あなたはハッシュ変更を必要とせず、ただすぐにスクロールバックするだけです
daniel.gindi

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

この方法の問題は、モバイルブラウザーの場合、スクロールが変更されていることが
わかります

1

これは可能ではないと思います。私の知る限り、ブラウザが変更さdocument.location.hashれたページにスクロールしないのは、ページ内にハッシュが存在しない場合だけです。

この記事はあなたの質問とは直接関係ありませんが、変化するブラウザの典型的な動作について説明していますdocument.location.hash


1

ハッシュパーサーでhashchangeイベントを使用する場合、リンクのデフォルトアクションを防止し、location.hashを変更して、要素のidプロパティとの違いを持たせるために1文字を追加できます。

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

パーフェクト!5時間後、ハッシュの再読み込みで問題を解決しようとしました...:/これが機能した唯一のものでした!! ありがとう!!!
Igor Trindade、2016年

1

これをここに追加します。関連性の高い質問はすべて、ここを指している重複としてマークされています…

私の状況はより簡単です:

  • ユーザーがリンク(a[href='#something'])をクリックする
  • クリックハンドラーは: e.preventDefault()
  • スムーズスクロール機能: $("html,body").stop(true,true).animate({ "scrollTop": linkoffset.top }, scrollspeed, "swing" );
  • その後 window.location = link;

このようにして、スクロールが発生し、場所が更新されてもジャンプしません。


0

これを行うもう1つの方法は、ビューポートの上部に非表示になっているdivを追加することです。このdivには、ハッシュがURLに追加される前にハッシュのIDが割り当てられます。そのため、スクロールを取得できません。


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

このソリューションは、実際の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

私のために働くより簡単な方法があります。基本的に、ハッシュが実際にHTMLで何であるかを思い出してください。名前タグへのアンカーリンクです。それがスクロールする理由です...ブラウザはアンカーリンクにスクロールしようとしています。だから、それをあげて!

  1. BODYタグのすぐ下に、このバージョンを配置します。
 <a name="home"> </a> <a name="firstsection"> </a> <a name="secondsection"> </a> <a name="thirdsection"> </a>
  1. セクションのdivに、IDではなくクラスの名前を付けます。

  2. 処理コードで、ハッシュマークを取り除き、ドットに置き換えます。

    var trimPanel = loadhash.substring(1); //ハッシュを失う

    var dotSelect = '。' + trimPanel; //ハッシュをドットに置き換えます

    $(dotSelect).addClass( "activepanel")。show(); //ハッシュに関連付けられたdivを表示します。

最後に、element.preventDefaultまたはreturn:falseを削除して、ナビゲーションを実行できるようにします。ウィンドウは上部にとどまり、ハッシュがアドレスバーのURLに追加され、正しいパネルが開きます。


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

0

ページでidをアンカーポイントのようなものとして使用し、ユーザーがURLの末尾に#somethingを追加し、独自に定義したアニメーションを使用してページをその#somethingセクションにスクロールさせるシナリオがある場合javascript関数、hashchangeイベントリスナーはそれを行うことができません。

たとえば、hashchangeイベントの直後にデバッガーを単に置く場合、たとえば、次のようなもの(まあ、私はjqueryを使用しますが、要点はわかります):

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

URLを変更してEnterボタンを押すとすぐに、ページが対応するセクションで停止し、その後すぐに、独自の定義されたスクロール関数がトリガーされ、そのセクションにスクロールして、ひどい。

私の提案は:

  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を削除してリセットします。

これでうまくいくはずです。


-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;
      }
    }
  });
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.