web-dev-qa-db-ja.com

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

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 +をサポートする必要があるので、私はそれが受け入れられた解決策ではないと思います:/

316
Rasmus

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

これは(安っぽい)ドキュメントです。

そしてここにその源があります。

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

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>
372
Ben Lesh

私の場合は、$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);
};
167
slugslog

ルーティングを変更する必要はなく、リンクを作成するときに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>

コメントで指摘したように##を使う必要はありません;-)

<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>
41
lincolnge

あなたがいつもルートを知っているなら、あなたは単にこのようにアンカーを追加することができます:

href="#/route#anchorID

ここでrouteは現在の角度付きルートで、anchorIDはページのどこかの<a id="anchorID">と一致します。

19
cab1113

これは私たちが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に接続されていてリフレッシュが本質的にアプリを壊すことになるので、これは私にとってはうまくいきました。

14
KhalilRavanna

$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)を考慮する必要がある場合は、(メインモジュールの実行関数で)次のように$ anchorScrollのオフセットを設定できます。

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

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

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

角度付きルートのハッシュ接頭辞を設定しようとします$locationProvider.hashPrefix('!')

完全な例:

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

これは古い投稿ですが、私は長い間さまざまな解決策を研究していたので、もう1つ簡単な解決策を共有したいと思いました。 target="_self"タグに<a>を追加するだけで修正されました。リンクが機能し、ページ上の適切な場所に案内されます。

ただし、AngularはまだURLに#を付けていくらか奇妙な表現をしているので、ナビゲーションに戻るボタンを使用するなどしてこのメ​​ソッドを使用した後で問題が発生する可能性があります。

5
Benjamin

これはngViewの新しい属性かもしれませんが、angular-route属性と 'double-hashes'を使ってngView 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>
4
michael

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

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

これは(ハードコードされた "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

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>
2
MrFlo
<a href="/#/#faq-1">Question 1</a>
<a href="/#/#faq-2">Question 2</a>
<a href="/#/#faq-3">Question 3</a>
2
felipe_dmz

スクロール機能を簡単に手に入れましょう。追加機能として アニメーション/スムーズスクロール もサポートしています。 Angular Scroll libraryの詳細:

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

バワー bower install --save angular-scroll

npm npm install --save angular-scroll

最小化バージョン - 9kbのみ

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

スクロールスパイ - はい

ドキュメンテーション - excellent

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

お役に立てれば。

1
Akash

Angularアプリをアンカーオポンの読み込みまでスクロールさせようとして、$ routeProviderのURL書き換えルールに遭遇しました。

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

  1. Angular appモジュールの.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
Stoyan Kenderov

ng-clickを使用したくない場合は、これに代わる解決策があります。現在の状態に基づいて正しいURLを生成するためにfilterを使用します。私の例では ui.router を使っています。

利点は、リンクが移動した場所をユーザーが確認できることです。

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

フィルター

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

これが常に機能するかどうかは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
Brian

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
Edmar Miyake

@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();
        }
    }
});
1
ThatMSG

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

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

それで私は、インデックスページから始めるようにページに通知し、それから伝統的なアンカーを使う必要があることに気づきました。

0
windmaomao

ルート変更時にはページの一番上までスクロールします。

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

このコードをあなたのコントローラーに置いてください。

0
Praveen M P

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

0

私の頭の中では@slugslogがそれを持っていました、しかし私は一つのことを変えるでしょう。代わりにreplaceを使用するので、元に戻す必要はありません。

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

ドキュメント "Replaceメソッド"を検索

0
Jackie

私は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をまったく変更する必要はありませんでした。

0

https://code.angularjs.org/1.4.10/docs/api/ngRoute/provider/ $ routeProviderを参照してください。

[reloadOnSearch = true] - {boolean =} - $ location.search()または$ location.hash()だけが変更されたときにルートをリロードします。

これをfalseに設定すると、上記のすべてがなくてもうまくいきました。

0
geekdenz