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 +をサポートする必要があるので、私はそれが受け入れられた解決策ではないと思います:/
あなたは $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>
私の場合は、$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);
};
ルーティングを変更する必要はなく、リンクを作成するときに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>
あなたがいつもルートを知っているなら、あなたは単にこのようにアンカーを追加することができます:
href="#/route#anchorID
ここでroute
は現在の角度付きルートで、anchorID
はページのどこかの<a id="anchorID">
と一致します。
これは私たちがDOMを扱っているのでもっとAngular-yに見えるディレクティブを使った私の解決策でした:
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();
});
};
});
<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に接続されていてリフレッシュが本質的にアプリを壊すことになるので、これは私にとってはうまくいきました。
$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;
});
私は私のアプリのルートロジックでこれを回避しました。
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;
}
});
}
角度付きルートのハッシュ接頭辞を設定しようとします$locationProvider.hashPrefix('!')
完全な例:
angular.module('app', [])
.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider){
$routeProvider.when( ... );
$locationProvider.hashPrefix('!');
}
])
これは古い投稿ですが、私は長い間さまざまな解決策を研究していたので、もう1つ簡単な解決策を共有したいと思いました。 target="_self"
タグに<a>
を追加するだけで修正されました。リンクが機能し、ページ上の適切な場所に案内されます。
ただし、AngularはまだURLに#を付けていくらか奇妙な表現をしているので、ナビゲーションに戻るボタンを使用するなどしてこのメソッドを使用した後で問題が発生する可能性があります。
これはngViewの新しい属性かもしれませんが、angular-route
属性と 'double-hashes'を使ってngView autoscroll
で動作するようにアンカーハッシュリンクを取得することができました。
(以下のコードはアンギュラストラップで使用されました)
<!-- 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>
私はこうすることができます:
<li>
<a href="#/#about">About</a>
</li>
これは(ハードコードされた "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);
}
}
};
});
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>
<a href="/#/#faq-1">Question 1</a>
<a href="/#/#faq-2">Question 2</a>
<a href="/#/#faq-3">Question 3</a>
スクロール機能を簡単に手に入れましょう。追加機能として アニメーション/スムーズスクロール もサポートしています。 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/
お役に立てれば。
Angularアプリをアンカーオポンの読み込みまでスクロールさせようとして、$ routeProviderのURL書き換えルールに遭遇しました。
長い実験の後、私はこれに落ち着きました:
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();
}
}
});
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;
};
}])
これが常に機能するかどうかは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>
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>
@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();
}
}
});
上記の解決策のどれも私にはうまくいきませんが、私はこれを試してみました、そしてそれはうまくいきました、
<a href="#/#faq-1">Question 1</a>
それで私は、インデックスページから始めるようにページに通知し、それから伝統的なアンカーを使う必要があることに気づきました。
ルート変更時にはページの一番上までスクロールします。
$scope.$on('$routeChangeSuccess', function () {
window.scrollTo(0, 0);
});
このコードをあなたのコントローラーに置いてください。
Angularjsアプリケーションでハッシュナビゲーションが機能しないことがあり、ブートストラップjquery javascriptライブラリがこのタイプのナビゲーションを広範囲に使用するため、アンカータグにtarget="_self"
を追加しています。例えば<a data-toggle="tab" href="#id_of_div_to_navigate" target="_self">
私の頭の中では@slugslogがそれを持っていました、しかし私は一つのことを変えるでしょう。代わりにreplaceを使用するので、元に戻す必要はありません。
$scope.scrollTo = function(id) {
var old = $location.hash();
$location.hash(id).replace();
$anchorScroll();
};
ドキュメント "Replaceメソッド"を検索
私は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をまったく変更する必要はありませんでした。
https://code.angularjs.org/1.4.10/docs/api/ngRoute/provider/ $ routeProviderを参照してください。
[reloadOnSearch = true] - {boolean =} - $ location.search()または$ location.hash()だけが変更されたときにルートをリロードします。
これをfalseに設定すると、上記のすべてがなくてもうまくいきました。