AngularJSでアンカーハッシュリンクを処理する方法


318

AngularJSでアンカーハッシュリンクをうまく処理する方法を知っている人はいますかますか?

簡単なFAQページの次のマークアップがあります

<a href="#faq-1">Question 1</a>
<a href="#faq-2">Question 2</a>
<a href="#faq-3">Question 3</a>

<h3 id="faq-1">Question 1</h3>
<h3 id="faq-2">Question 2</h3>
<h3 id="fa1-3">Question 3</h3>

上記のリンクのいずれかをクリックすると、AngularJSがインターセプトして、まったく別のページ(私の場合、リンクに一致するルートがないため、404ページ)にルーティングされます。

私の最初の考えは、「/ faq /:chapter」に一致するルートを作成し、対応するコントローラーチェックで作成することでした$routeParams.chapter一致する要素の後にし、jQueryを使用してその要素までスクロールすることでした。

しかし、AngularJSは再び私にたわごとを与え、とにかくページの上部にスクロールします。

だから、ここの誰かが過去に似たようなことをし、それに対する良い解決策を知っていますか?

編集:html5Modeに切り替えると問題が解決するはずですが、とにかくIE8 +をサポートする必要があるため、受け入れられない解決策ではないかと心配しています:/


ng-href=""代わりに使用することをお勧めします。
ピギーバック

10
ng-hrefは、URLにng-modelにバインドする必要がある動的データが含まれている場合にのみ適用できると思います。種類驚異のIそれはIDのへのリンクの無視する場合は、locationProviderにhashPrefixを割り当てた場合:docs.angularjs.org/guide/dev_guide.services.$location
アダム

Adamはng-hrefの使用については正しいです。
Rasmus


:これは、新しい角度のための問題であるstackoverflow.com/questions/36101756/...
phil294

回答:


375

あなたが探してい$anchorScroll()ます。

これが(くだらない)ドキュメントです。

そして、これがソースです。

基本的には、それを注入してコントローラーで呼び出すだけで、次の場所にあるIDを持つ任意の要素にスクロールします $location.hash()

app.controller('TestCtrl', function($scope, $location, $anchorScroll) {
   $scope.scrollTo = function(id) {
      $location.hash(id);
      $anchorScroll();
   }
});

<a ng-click="scrollTo('foo')">Foo</a>

<div id="foo">Here you are</div>

ここにデモンストレーションするプランカーがあります

編集:これをルーティングで使用するには

通常のように角度ルーティングを設定し、次のコードを追加します。

app.run(function($rootScope, $location, $anchorScroll, $routeParams) {
  //when the route is changed scroll to the proper element.
  $rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
    $location.hash($routeParams.scrollTo);
    $anchorScroll();  
  });
});

リンクは次のようになります。

<a href="#/test?scrollTo=foo">Test/Foo</a>

これはルーティングと$ anchorScrollによるスクロールを示すPlunkerです

そしてさらに簡単:

app.run(function($rootScope, $location, $anchorScroll) {
  //when the route is changed scroll to the proper element.
  $rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
    if($location.hash()) $anchorScroll();  
  });
});

リンクは次のようになります。

<a href="#/test#foo">Test/Foo</a>

5
ルーティングを追加するときに問題が発生する可能性があります。ngViewを追加すると、URLのハッシュの各変更によりルートの再読み込みがトリガーされます...この例では、ルーティングはなく、URLは現在のアイテムを反映していません...しかし、$ anchorScrollをポイントしていただきありがとうございます
Valentyn Shybanov

1
@ blesh、location.hash(X)を呼び出すと、ルーティングによってビューが制御されるため、ページが変更されます。
dsldsl 2013年

24
このソリューションにより、アプリケーション全体が再レンダリングされます。

2
@dsldsl && @OliverJosephAsh:$ location.hash()はページを再読み込みしません。もしそうなら、何か他のことが起こっています。これは、ページが読み込まれるときに書き出される時間が同じプランクです。変更されないことがわかります。 ルートを再ロードせずに現在のページのアンカータグまでスクロールする場合は、次のようにします。定期リンク<a href="#foo">foo</a>。私のコードサンプルは、routechangeでidへのスクロールを表示することでした。
Ben Lesh

4
@blesh:目的のセクションまでスクロールした後、ハッシュも削除することをお勧めします。これにより、URLが実際に存在してはならないもので汚染されなくなります。これを使用してください: $location.search('scrollTo', null)
kumarharsh

168

私の場合、を変更すると、ルーティングロジックが機能し始めていることに気付きました$location.hash()。次のトリックはうまくいった。

$scope.scrollTo = function(id) {
    var old = $location.hash();
    $location.hash(id);
    $anchorScroll();
    //reset to old to keep any additional routing logic from kicking in
    $location.hash(old);
};

5
そのための素晴らしい感謝、ルーティングロジックは、@ bleshの.run(...)ソリューションを使用している場合でも、動作を完全に拒否し、これによりソートされます。
ljs 2013

8
あなたの「古いハッシュを保存する」トリックは、絶対的な命の恩人です。ルートをクリーンに保ちながら、ページのリロードを防ぎます。素晴らしいアイデア!
ThisLanham 2013

2
良いですね。しかし、ソリューションを実装した後、URLはターゲットIDの値を更新していません。
Mohamed Hussain 2014年

1
私はMohamedと同じ経験をしました...確かにリロードを停止しましたが、ハッシュレスルートを表示します(そして$ anchorScrollは効果がありませんでした)。1.2.6うーん。
マイケル2014年

3
使用します$location.hash(my_id); $anchorScroll; $location.hash(null)。それはリロードを防ぎ、old変数を管理する必要はありません。
MFB 2015年

53

ルーティングを変更する必要はなくtarget="_self"、リンクを作成するときに使用する必要があるものだけです。

例:

<a href="#faq-1" target="_self">Question 1</a>
<a href="#faq-2" target="_self">Question 2</a>
<a href="#faq-3" target="_self">Question 3</a>

次のようにhtml要素でid属性を使用します。

<h3 id="faq-1">Question 1</h3>
<h3 id="faq-2">Question 2</h3>
<h3 id="faq-3">Question 3</h3>

コメントで指摘/言及されている##を使用する必要はありません;-)


1
これは私にとっては
うまくいき

6
この解決策をありがとう。ただし、target = "_ self"で十分です。#を2倍にする必要はありません
Christophe P

5
target = "_ self"は間違いなく最良の答えです(Christophe Pが指摘したように、#を2倍にする必要はありません)。これは、Html5Modeがオンでもオフでも機能します。
ニューマン

3
シンプルで完全。さらに別の角度依存を追加する必要なしに私のために働いた。
adeady 2016年

4
これが正しい解決策です。角張った$ anchorScrollサービスを使用する必要はありません。タグのドキュメントを参照してください:https
//developer.mozilla.org/en/docs/Web/HTML/Element/a

41
<a href="##faq-1">Question 1</a>
<a href="##faq-2">Question 2</a>
<a href="##faq-3">Question 3</a>

<h3 id="faq-1">Question 1</h3>
<h3 id="faq-2">Question 2</h3>
<h3 id="faq-3">Question 3</h3>

驚くばかり!はるかに簡単な解決策ですが、別のページのアンカーにリンクする方法はありますか?(すなわち/ products#books)
8bithero 2014年

AngularJSのソリューション(/ products ## books)と同じだと思う
リンカーン2014年

1
私の経験では、href = "##"は$ anchorScrollが注入されたときにのみ機能します。
ブライアンパーク

9
これは比較的シンプルに見えますが、機能しません:-(
brykneval

4
私はtarget = "_ self"を追加し、ページ内のすべてのタイプのナビゲーション(スライダーの読み取り、別のセクションへの移動など)の魅力のように機能しました。この素晴らしくて最も単純なトリックを共有していただきありがとうございます。
Kumar Nitesh

19

ルートがわかっている場合は、次のようにアンカーを追加するだけです。

href="#/route#anchorID

where routeは現在の角度ルートであり、ページのどこかにanchorID一致<a id="anchorID">します


1
これは通常のAngularJSルート変更をトリガーするため、お勧めしません。私の場合、FAQ /ヘルプページのYouTubeビデオがリロードされたので、それは非常に視覚的でした。
Robin Andersson、

9
@RobinWassén-AnderssonをreloadOnSearch: falseルート設定でそのルートに指定すると、angularはルートの変更をトリガーせず、IDまでスクロールします。aタグで指定された完全なルートと組み合わせると、これが最も簡単で最も簡単な解決策だと思います。
エリーゼ

ありがとうございました。これは私を助けました。アプリでカスタムルートを使用していないので、href = "#/#anchor-name"を実行するとうまくいきました。
ヨルダン

14

$anchorScroll これでうまくいきますが、Angularのより新しいバージョンでそれを使用するより良い方法があります。

さて、$anchorScrollあなたは変更する必要はありませんので、オプションの引数としてハッシュを受け入れる$location.hashすべてで。(ドキュメント

ルートにまったく影響を与えないため、これが最良のソリューションです。ngRouteを使用しているため、他のソリューションを機能させることができず、ルートは$location.hash(id)、設定するとすぐにリロードされます。$anchorScrollそのその魔法を実行できました。

これを使用する方法は次のとおりです...まず、ディレクティブまたはコントローラーで:

$scope.scrollTo = function (id) {
  $anchorScroll(id);  
}

そしてビューで:

<a href="" ng-click="scrollTo(id)">Text</a>

また、固定ナビゲーションバー(または他のUI)を考慮する必要がある場合は、次のように(メインモジュールのrun関数で)$ anchorScrollのオフセットを設定できます。

.run(function ($anchorScroll) {
   //this will make anchorScroll scroll to the div minus 50px
   $anchorScroll.yOffset = 50;
});

ありがとう。ルート変更と組み合わせたハッシュリンクの戦略をどのように実装しますか?例:このナビゲーションアイテムをクリックすると、別のビューが開き、idそのビューの特定の場所までスクロールします。
Kyle Vassella 2017年

せっかちに言ってください。もし機会があれば、私のStackの質問を一目で見ることができますか?ここでのあなたの答えは私にとても近づいたように感じますが、それを実装することはできません:stackoverflow.com/questions/41494330/…。私もngRouteAngularの新しいバージョンを使用しています。
カイルヴァセッラ2017年

その特定のケースを試していないのは残念ですが、$ location.search()または$ routeParamsを確認しましたか?おそらく、コントローラーの初期化でどちらかを使用できます。scrollTo検索パラメーターがURLにある場合、コントローラーは上記の$ anchorScrollを使用してページをスクロールできます。
レベッカ

2
IDを$ anchorScrollに直接渡すと、ルートが/ contact#contactなどから/ contactだけに変更されました。これは受け入れられた答えであるはずです。
RandomUs1r 2017年

12

これは、DOMを扱っているため、よりAngular-yに見えるディレクティブを使用した私の解決策でした。

ここでPlnkr

github

コード

angular.module('app', [])
.directive('scrollTo', function ($location, $anchorScroll) {
  return function(scope, element, attrs) {

    element.bind('click', function(event) {
        event.stopPropagation();
        var off = scope.$on('$locationChangeStart', function(ev) {
            off();
            ev.preventDefault();
        });
        var location = attrs.scrollTo;
        $location.hash(location);
        $anchorScroll();
    });

  };
});

HTML

<ul>
  <li><a href="" scroll-to="section1">Section 1</a></li>
  <li><a href="" scroll-to="section2">Section 2</a></li>
</ul>

<h1 id="section1">Hi, I'm section 1</h1>
<p>
Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro. De carne lumbering animata corpora quaeritis. 
 Summus brains sit​​, morbo vel maleficia? De apocalypsi gorger omero undead survivor dictum mauris. 
Hi mindless mortuis soulless creaturas, imo evil stalking monstra adventus resi dentevil vultus comedat cerebella viventium. 
Nescio brains an Undead zombies. Sicut malus putrid voodoo horror. Nigh tofth eliv ingdead.
</p>

<h1 id="section2">I'm totally section 2</h1>
<p>
Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro. De carne lumbering animata corpora quaeritis. 
 Summus brains sit​​, morbo vel maleficia? De apocalypsi gorger omero undead survivor dictum mauris. 
Hi mindless mortuis soulless creaturas, imo evil stalking monstra adventus resi dentevil vultus comedat cerebella viventium. 
Nescio brains an Undead zombies. Sicut malus putrid voodoo horror. Nigh tofth eliv ingdead.
</p>

$ anchorScrollサービスを使用しました。ハッシュの変更に伴うページ更新を打ち消すために、locationChangeStartイベントをキャンセルしました。ng-switchに接続されたヘルプページがあり、更新するとアプリが本質的に壊れるので、これは私にとってはうまくいきました。


ディレクティブソリューションが気に入っています。ただし、どのようにして別のページをロードし、同時にアンカーの場所までスクロールする必要がありましたか。angularjsがなければ、通常はhref = "location#hash"になります。しかし、ディレクティブはページのリロードを防ぎます。
CMCDragonkai 2013

@CMCDragonkaiと私のディレクティブ、私は$ anchorScrollの呼び出しを利用しているのでわかりません。これは、現在ページ上の要素へのスクロールのみを処理しているように見えます。ページの変更を伴う何かを取得するには、$ locationまたは$ windowをいじる必要があるかもしれません。
KhalilRavanna 2013

2
locationChangeStartイベントからサブスクライブを解除する必要があります:var off = scope。$ on( '$ locationChangeStart'、function(ev){off(); ev.preventDefault();});

@EugeneTskhovrebovのグッドキャッチ、私は先に進み、それを編集の回答に追加しました。
KhalilRavanna 2014年

5

角度ルートのハッシュプレフィックスを設定してみてください $locationProvider.hashPrefix('!')

完全な例:

angular.module('app', [])
  .config(['$routeProvider', '$locationProvider', 
    function($routeProvider, $locationProvider){
      $routeProvider.when( ... );
      $locationProvider.hashPrefix('!');
    }
  ])

これは結果には影響しません。でもそれは甘いでしょう。
Rasmus 2013

2
本気ですか。なぜこれが機能しないのですか?ハッシュプレフィックスが!の場合、ハッシュルーティングは#!pageである必要があります。したがって、AngularJSは#hashだけを検出し、スクロールを自動的にアンカーし、HTML5モードのURLとハッシュモードのURLの両方で機能します。
CMCDragonkai 2013

5

アプリのルートロジックでこれを回避しました。

function config($routeProvider) {
  $routeProvider
    .when('/', {
      templateUrl: '/partials/search.html',
      controller: 'ctrlMain'
    })
    .otherwise({
      // Angular interferes with anchor links, so this function preserves the
      // requested hash while still invoking the default route.
      redirectTo: function() {
        // Strips the leading '#/' from the current hash value.
        var hash = '#' + window.location.hash.replace(/^#\//g, '');
        window.location.hash = hash;
        return '/' + hash;
      }
    });
}

1
これは機能しません:エラー:[$ injector:modulerr]次の理由によりモジュール角度のインスタンス化に失敗しました:エラー:when()の無効な 'ハンドラ'
geoidesic

5

これは古い投稿ですが、さまざまなソリューションの調査に長い時間を費やしたので、もう1つのシンプルなソリューションを共有したいと思いました。タグに追加target="_self"するだけで<a>修正されました。リンクが機能し、ページ上の適切な場所に移動します。

ただし、Angularは依然としてURLの#に奇妙さを注入しているため、このメソッドを使用した後にナビゲーションなどの戻るボタンを使用すると問題が発生する可能性があります。


4

これはngViewの新しい属性である可能性がありますがangular-routengView autoscroll属性と「ダブルハッシュ」を使用して動作するようにアンカーハッシュリンクを取得することができました。

ngView(自動スクロールを参照)

(次のコードは角度ストラップで使用されました)

<!-- use the autoscroll attribute to scroll to hash on $viewContentLoaded -->    
<div ng-view="" autoscroll></div>

<!-- A.href link for bs-scrollspy from angular-strap -->
<!-- A.ngHref for autoscroll on current route without a location change -->
<ul class="nav bs-sidenav">
  <li data-target="#main-html5"><a href="#main-html5" ng-href="##main-html5">HTML5</a></li>
  <li data-target="#main-angular"><a href="#main-angular" ng-href="##main-angular" >Angular</a></li>
  <li data-target="#main-karma"><a href="#main-karma" ng-href="##main-karma">Karma</a></li>
</ul>

3

私はそうすることができます:

<li>
<a href="#/#about">About</a>
</li>

2

これは、指定された要素にスクロールするカスタムディレクティブを作成することによる一種のダーティな回避策です(ハードコードされた「faq」を使用)

app.directive('h3', function($routeParams) {
  return {
    restrict: 'E',
    link: function(scope, element, attrs){        
        if ('faq'+$routeParams.v == attrs.id) {
          setTimeout(function() {
             window.scrollTo(0, element[0].offsetTop);
          },1);        
        }
    }
  };
});

http://plnkr.co/edit/Po37JFeP5IsNoz5ZycFs?p=preview


これは確かに機能しますが、あなたが言ったように汚れています。とても汚い。他の誰かがもっときれいな解決策を考え出すことができるかどうか見てみましょう、そうでなければ私はこれで行かなければならないでしょう。
Rasmus 2013

Angularにはすでにを介してscrollTo機能が組み込まれて$anchorScrollいます。私の答えをご覧ください。
Ben Lesh 2013

汚れが少なくなるようにプランカーを変更しました。$ location.path()を使用するため、ソースにハードコーディングされた「faq」はありません。また、$ anchorScrollを使用しようとしましたが、ルーティングが原因で機能しないようです...
Valentyn Shybanov

2
<a href="/#/#faq-1">Question 1</a>
<a href="/#/#faq-2">Question 2</a>
<a href="/#/#faq-3">Question 3</a>

2

使用したくない場合はng-click、ここで代替ソリューションを使用できます。filter現在の状態に基づいて正しいURLを生成するために使用します。私の例ではui.routerを使用しています

利点は、ユーザーがリンクにカーソルを合わせると表示されることです。

<a href="{{ 'my-element-id' | anchor }}">My element</a>

フィルター:

.filter('anchor', ['$state', function($state) {
    return function(id) {
        return '/#' + $state.current.url + '#' + id;
    };
}])

2

ng-routeを使用した私のソリューションは、次の単純なディレクティブでした。

   app.directive('scrollto',
       function ($anchorScroll,$location) {
            return {
                link: function (scope, element, attrs) {
                    element.click(function (e) {
                        e.preventDefault();
                        $location.hash(attrs["scrollto"]);
                        $anchorScroll();
                    });
                }
            };
    })

HTMLは次のようになります。

<a href="" scrollTo="yourid">link</a>

あなたのような属性を指定する必要はないscroll-to="yourid"とディレクティブ名scrollTo(およびアクセスなどの属性attrs["scrollTo"]にバインドされるexplicite jQueryの算入せずに、ハンドラを他にすることはしている?element.on('click', function (e) {..})
Theodros Zelleke

1

あなたはanchorScrollを使用しようとすることができます。

したがって、コントローラは次のようになります。

app.controller('MainCtrl', function($scope, $location, $anchorScroll, $routeParams) {
  $scope.scrollTo = function(id) {
     $location.hash(id);
     $anchorScroll();
  }
});

そしてビュー:

<a href="" ng-click="scrollTo('foo')">Scroll to #foo</a>

...アンカーIDの秘密はありません:

<div id="foo">
  This is #foo
</div>

1

Angularアプリをアンカーオポンロードまでスクロールさせ、$ routeProviderのURL書き換えルールにぶつかりました。

長い実験の後、私はこれに落ち着きました:

  1. Angularアプリモジュールの.run()セクションからdocument.onloadイベントハンドラーを登録します。
  2. ハンドラーで、いくつかの文字列操作を実行することにより、元のアンカータグがどうなっていたかを調べます。
  3. 削除されたアンカータグでlocation.hashをオーバーライドします(これにより、$ routeProviderはすぐに「#/」ルールで再度上書きします。ただし、AngularがURL 4で行われていることと同期しているので問題ありません)。 $ anchorScroll()。

angular.module("bla",[]).}])
.run(function($location, $anchorScroll){
         $(document).ready(function() {
	 if(location.hash && location.hash.length>=1)    		{
			var path = location.hash;
			var potentialAnchor = path.substring(path.lastIndexOf("/")+1);
			if ($("#" + potentialAnchor).length > 0) {   // make sure this hashtag exists in the doc.                          
			    location.hash = potentialAnchor;
			    $anchorScroll();
			}
		}	 
 });


1

これが常に機能するかどうかは100%わかりませんが、私のアプリケーションでは、これによって期待される動作が得られます。

あなたがABOUTページにいて、あなたが次のルートを持っているとしましょう:

yourApp.config(['$routeProvider', 
    function($routeProvider) {
        $routeProvider.
            when('/about', {
                templateUrl: 'about.html',
                controller: 'AboutCtrl'
            }).
            otherwise({
                redirectTo: '/'
            });
        }
]);

今、あなたのHTMLで

<ul>
    <li><a href="#/about#tab1">First Part</a></li>
    <li><a href="#/about#tab2">Second Part</a></li>
    <li><a href="#/about#tab3">Third Part</a></li>                      
</ul>

<div id="tab1">1</div>
<div id="tab2">2</div>
<div id="tab3">3</div>

結論として

アンカーが私のためにトリックをする前にページ名を含めること。あなたの考えを教えてください。

欠点

これにより、ページが再レンダリングされ、アンカーまでスクロールします。

更新

より良い方法は、以下を追加することです:

<a href="#tab1" onclick="return false;">First Part</a>

1

スクロール機能を簡単に入手できます。また、追加機能としてアニメーション/スムーズスクロールもサポートしています。Angular Scrollライブラリの詳細:

Github - https://github.com/oblador/angular-scroll

バウアーbower install --save angular-scroll

npmnpm install --save angular-scroll

圧縮バージョン -わずか9kb

スムーズスクロール(アニメーションスクロール) -はい

スクロールスパイ -はい

ドキュメンテーション -優れた

デモ - http://oblador.github.io/angular-scroll/

お役に立てれば。


1

@Stoyanに基づいて、私は次の解決策を思いつきました:

app.run(function($location, $anchorScroll){
    var uri = window.location.href;

    if(uri.length >= 4){

        var parts = uri.split('#!#');
        if(parts.length > 1){
            var anchor = parts[parts.length -1];
            $location.hash(anchor);
            $anchorScroll();
        }
    }
});

0

ルート変更時に、ページの上部にスクロールします。

 $scope.$on('$routeChangeSuccess', function () {
      window.scrollTo(0, 0);
  });

このコードをコントローラーに配置します。


0

私の考えでは@slugslogにはそれがありましたが、私は1つ変更します。代わりに置換を使用するので、元に戻す必要はありません。

$scope.scrollTo = function(id) {
    var old = $location.hash();
    $location.hash(id).replace();
    $anchorScroll();
};

「置換方法」のドキュメント検索


0

上記の解決策はどれも私にとってはうまくいきませんが、私はこれを試してみましたが、うまくいきました、

<a href="#/#faq-1">Question 1</a>

そのため、インデックスページから始めて従来のアンカーを使用するようにページに通知する必要があることに気付きました。


0

angularjsアプリケーションのハッシュナビゲーションが機能しない場合があり、ブートストラップjquery javascriptライブラリはこのタイプのナビゲーションを広範囲に使用して、target="_self"アンカータグに追加して機能させます。例えば<a data-toggle="tab" href="#id_of_div_to_navigate" target="_self">



0

AngularJS 1.3.15を使用していますが、特別なことをする必要はないようです。

https://code.angularjs.org/1.3.15/docs/api/ng/provider/ $ anchorScrollProvider

だから、以下は私のhtmlで私のために働きます:

<ul>
  <li ng-repeat="page in pages"><a ng-href="#{{'id-'+id}}">{{id}}</a>
  </li>
</ul>
<div ng-attr-id="{{'id-'+id}}" </div>

コントローラーやJavaScriptに変更を加える必要はまったくありませんでした。

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