AngularJS 中如何处理锚文本哈希链接

有人知道如何在AngularJS中很好地处理锚文本或者哈希链接吗?

对于一个简单的faq页面,我有以下标记

Question 1
Question 2
Question 3


Question 1

Question 2

Question 3

当点击上面的任何链接时,AngularJS会拦截并将我路由到一个完全不同的页面(在我的例子中,一个404页,因为没有路由匹配这些链接)。

我的第一个想法是创建一个匹配“/常见问题:章”的路由,并在对应的控制器中检查匹配元素后的$routeParams.chapter,然后使用jQuery向下滚动到它。

但是AngularJS又把我搞砸了,反正就是滚动到页面顶部。

有人在过去做过类似的事情并且知道一个好的解决方法吗?

编辑:切换到 html5Mode 应该解决我的问题,但我们有点必须支持 IE8+ 无论如何,所以我担心这不是一个可接受的解决方案:/

233483 次浏览

这里是一种肮脏的工作方法,通过创建自定义指令,将滚动到指定的元素(硬编码的“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);
}
}
};
});

< a href = " http://plnkr.co/edit/Po37JFeP5IsNoz5ZycFs?p =预览”rel = " nofollow”> http://plnkr.co/edit/Po37JFeP5IsNoz5ZycFs?p =预览< / >

尝试为angular路由$locationProvider.hashPrefix('!')设置一个哈希前缀

完整的例子:

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

你正在寻找$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>

< a href = " http://plnkr.co/edit/PCO051UJS8EHbdkmFV40?p=preview" rel="noreferrer">这是一个柱塞来演示

EDIT:用于路由

像往常一样设置angular路由,然后添加以下代码。

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>

这是一个Plunker演示了使用路由和$anchorScroll滚动

更简单的是:

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

这是我使用一个指令的解决方案,它看起来更像angular,因为我们处理的是DOM:

< a href = " http://plnkr.co/edit/Sl2V4u3tVzsqEj7ttgNi?p=preview" rel="nofollow noreferrer">Plnkr over here

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


};
});

超文本标记语言

<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的帮助页面,而刷新基本上会破坏应用程序。

如果你总是知道路径,你可以像这样简单地追加锚:

href="#/route#anchorID

其中route是当前的角路由,而anchorID匹配页面某处的<a id="anchorID">

你可以尝试使用anchorScroll

< a href = " http://plnkr.co/edit/QeU4oPJ1f0tx5b0tyFFX?p =预览”rel = " nofollow”> < / >示例

所以控制器会是:

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

这可能是ngView的一个新属性,但我已经能够使用ngView autoscroll属性和'double-hashes'让它锚定哈希链接与angular-route一起工作。

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>

我在我的应用程序的路由逻辑中解决了这个问题。

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

我试图让我的Angular应用在加载时滚动到一个锚点,却遇到了$routeProvider的URL重写规则。

经过长时间的试验,我确定了这一点:

    注册一个文档。的.run()部分中的Onload事件处理程序 Angular的app模块。李< / >
  1. 在处理程序中找出原始的 锚标签应该是通过做一些字符串操作。
  2. <李>覆盖位置。哈希与剥离锚标记(其中 导致$routeProvider立即再次覆盖它 “# /”规则。但这没关系,因为Angular现在与 调用$anchorScroll()的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();
}
}
});

我不能100%确定这是否一直工作,但在我的应用程序中,这给了我预期的行为。

假设你在关于页面上,你有以下路由:

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>

在路径更改时,它将滚动到页面顶部。

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

把这段代码放在控制器上。

在我的脑海中@ slugsllog已经有了,但我要改变一件事。我会用replace代替,这样你就不用回调了。

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

文档搜索“替换方法”

上面的方法对我都没用,但我刚试了一下,而且很管用,

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

所以我意识到我需要通知页面从索引页开始,然后使用传统的锚。

我使用的是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做任何更改。

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

这是一个老帖子,但我花了很长时间研究各种解决方案,所以我想分享一个更简单的解决方案。只是添加target="_self"<a>标签为我修复了它。该链接工作,并把我带到页面上的适当位置。

然而,Angular仍然在URL中注入了一些奇怪的#,所以在使用这个方法后,你可能会在使用返回按钮导航等方面遇到麻烦。

不需要改变任何路由或其他任何东西,只需要在创建链接时使用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>

并在你的超文本标记语言元素中像这样使用id属性:

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

在注释中没有必要使用##;-)

我可以这样做:

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

$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函数中):

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

轻松获得滚动功能。它还支持动画/平滑滚动作为附加特性。角滚动库的详细信息:

__abc0 - __abc1

鲍尔: bower install --save angular-scroll

__abc1: __abc0

Minfied版本 -只有9kb

平滑滚动(动画滚动) -是的

滚动的间谍 -是的

文档 -很好

__abc0 - __abc1

希望这能有所帮助。

有时候在angularjs应用程序中哈希导航不工作,并且bootstrap jquery javascript库大量使用这种类型的导航,为了使它工作,添加target="_self"到锚标记。 例如<a data-toggle="tab" href="#id_of_div_to_navigate" target="_self"> < / p >

看到https://code.angularjs.org/1.4.10/docs/api/ngRoute/provider/ routeProvider美元

[reloadOnSearch=true] - {boolean=} -仅当$location.search()或$location.hash()发生变化时重新加载路由。

对我来说,把这个设置为false就可以了。

如果你不喜欢使用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;
};
}])

我使用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>

基于@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();
}
}
});

试试这个可以解决锚的问题。

app.run(function($location, $anchorScroll){
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();


document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
});