ChatGPT解决这个技术问题 Extra ChatGPT

如何在 AngularJS 中处理锚散列链接

你们中有人知道如何在 AngularJS 中很好地处理锚散列链接吗?

我有一个简单的常见问题页面的以下标记

<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+,所以我担心这不是一个可接受的解决方案:/

我认为 angular 建议改用 ng-href=""
我认为 ng-href 仅适用于 url 包含需要绑定到 ng-model 的动态数据。我有点想知道您是否将 hashPrefix 分配给 locationProvider,如果它会忽略链接到 ID:docs.angularjs.org/guide/dev_guide.services.$location
Adam 在 ng-href 用法上是正确的。
Anchor links in Angularjs? 的可能重复项
这也是新 Angular 的一个问题:stackoverflow.com/questions/36101756/…

g
georgeawg

您正在寻找 $anchorScroll()

Here's the (crappy) documentation.

And here's the source.

基本上你只需注入它并在你的控制器中调用它,它就会滚动到任何具有在 $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>

Here is a plunker to demonstrate

编辑:将此与路由一起使用

像往常一样设置您的角度路由,然后只需添加以下代码。

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 demonstrating scrolling with routing and $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>

添加路由时可能会出现问题:如果添加 ngView url 哈希的每次更改都会触发路由重新加载...在您的示例中,没有路由并且 url 不反映当前项目...但是感谢您指向 $anchorScroll
@blesh,调用 location.hash(X) 会更改页面,因为路由控制视图。
此解决方案导致我的整个应用程序重新渲染。
@dsldsl && @OliverJosephAsh: $location.hash() 不会重新加载页面。如果是的话,还有其他事情正在发生。 Here is the same plunk with the time being written out as the page is loading, you'll see it doesn't change 如果您想滚动到当前页面上的锚标记而不重新加载路线,您只需执行常规链接 <a href="#foo">foo</a>。我的代码示例是在 routechange 上显示滚动到一个 id。
@blesh:我建议您在滚动到所需部分后也删除哈希,这样 URL 就不会被真正不应该存在的东西污染。使用这个:$location.search('scrollTo', null)
D
Drewness

就我而言,我注意到如果我修改了 $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);
};

非常感谢,即使使用@blesh 的 .run(...) 解决方案,路由逻辑也绝对拒绝运行,这对其进行了排序。
您的“保存旧哈希”技巧绝对是救命稻草。它可以防止页面重新加载,同时保持路由清洁。真棒的想法!
好东西。但是在实施您的解决方案后,url 不会更新目标 id 的值。
我和 Mohamed 有同样的经历......它确实停止了重新加载,但它显示了无哈希路由(并且 $anchorScroll 没有效果)。 1.2.6 嗯。
我使用 $location.hash(my_id); $anchorScroll; $location.hash(null)。它可以防止重新加载,我不必管理 old 变量。
M
Mauricio Gracia Gutierrez

创建链接时无需更改任何路由或其他任何内容只需使用 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>

没有必要使用评论中指出/提到的## ;-)


这对我不起作用,但这里的解决方案确实有效:stackoverflow.com/a/19367249/633107
感谢您提供此解决方案。但是 target="_self" 就足够了。不需要加倍#
target="_self" 绝对是最好的答案(如 Christophe P 所指出的,无需将 # 加倍)。无论 Html5Mode 是打开还是关闭,这都有效。
简单而完整。为我工作而无需添加另一个角度依赖。
这是正确的解决方案。无需涉及角度 $anchorScroll 服务。请参阅标签的文档:https://developer.mozilla.org/en/docs/Web/HTML/Element/a
l
lincolnge
<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 )
我认为它与AngularJS中的解决方案(/products##books)相同
根据我的经验, href="##" 仅在注入 $anchorScroll 时才有效。
这看起来相对简单,但它不起作用:-(
我添加了 target="_self" ,它对页面内的所有类型的导航(阅读滑块、转到不同的部分等等)都很有效。感谢您分享这个伟大而简单的技巧。
c
cab1113

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

href="#/route#anchorID

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


这会触发正常的 AngularJS 路由更改,因此不鼓励这样做。就我而言,这是非常直观的,因为重新加载了常见问题/帮助页面中的 YouTube 视频。
@RobinWassén-Andersson 通过在您的路线配置中为该路线指定 reloadOnSearch: false,角度不会触发路线更改,只会滚动到 id。结合 a 标记中指定的完整路由,我会说这是最简单、最直接的解决方案。
谢谢你。这对我有帮助。我没有在我的应用程序中使用任何自定义路由,所以执行 href="#/#anchor-name" 效果很好!
T
T J

$anchorScroll 适用于此,但在更新的 Angular 版本中有更好的方法来使用它。

现在,$anchorScroll 接受哈希作为可选参数,因此您根本不必更改 $location.hash。 (documentation)

这是最好的解决方案,因为它根本不影响路线。我无法让任何其他解决方案发挥作用,因为我正在使用 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;
});

谢谢。您将如何实施与路由更改相结合的哈希链接策略?示例:单击此导航项,这将打开一个不同的视图并向下滚动到该视图中的特定 id
抱歉纠缠..如果你有机会,你能看一下我的堆栈问题吗?我觉得您在这里的回答让我如此接近,但我无法实现它:stackoverflow.com/questions/41494330/…。我也在使用 ngRoute 和较新版本的 Angular。
很抱歉,我没有尝试过那种特殊情况...但是您看过 $location.search()$routeParams 吗?也许您可以在初始化控制器时使用其中的一个 - 如果您的 scrollTo 搜索参数在 URL 中,那么控制器可以使用上面的 $anchorScroll 来滚动页面。
通过将 Id 直接传递给 $anchorScroll,我的路线从 /contact#contact 变成了 /contact 。这应该是公认的答案恕我直言。
M
Michael

这是我使用指令的解决方案,它看起来更像 Angular-y,因为我们正在处理 DOM:

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

  };
});

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 使用我的指令,我不确定,因为我使用了对 $anchorScroll 的调用,它看起来只处理滚动到页面上当前的元素。您可能不得不使用 $location$window 来获得涉及页面更改的内容。
您需要取消订阅 locationChangeStart 事件: var off = scope.$on('$locationChangeStart', function(ev) { off(); ev.preventDefault(); });
很好@EugeneTskhovrebov,我继续在编辑中将其添加到答案中。
n
nakhli

尝试为角度路由设置哈希前缀 $locationProvider.hashPrefix('!')

完整示例:

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

这不影响结果。不过会很甜。
你确定吗。为什么这不起作用?如果哈希前缀是!,那么哈希路由应该是#!page。因此 AngularJS 应该检测它何时只是一个#hash,它应该自动锚定滚动并适用于 HTML5 模式 url 和哈希模式 url。
n
nicksanta

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

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

这不起作用:错误:[$injector:modulerr] 无法实例化模块角度,原因是:错误:when() 中的“处理程序”无效
B
Benjamin

这是一篇旧帖子,但我花了很长时间研究各种解决方案,所以我想分享一个更简单的解决方案。只需将 target="_self" 添加到 <a> 标记即可为我修复它。该链接有效,并将我带到页面上的正确位置。

但是,Angular 仍然会在 URL 中使用 # 注入一些奇怪的东西,因此在使用此方法后,使用后退按钮进行导航等可能会遇到麻烦。


m
michael

这可能是 ngView 的一个新属性,但我已经能够使用 ngView autoscroll 属性和“双哈希”使其锚散列链接与 angular-route 一起使用。

ngView (see autoscroll)

(以下代码与 angular-strap 一起使用)

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

T
T J

我可以这样做:

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

V
Valentyn Shybanov

这是通过创建将滚动到指定元素的自定义指令(使用硬编码的“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


这确实有效,但正如你所说,它很脏。很肮脏。让我们看看其他人是否可以提出更漂亮的解决方案,否则我将不得不这样做。
Angular 已经通过 $anchorScroll 内置了 scrollTo 功能,请参阅我的回答。
将 plunker 更改为不那么脏:它使用 $location.path() 因此源代码中没有硬编码的“faq”。并且还尝试使用 $anchorScroll,但似乎由于路由它不起作用......
f
felipe_dmz
<a href="/#/#faq-1">Question 1</a>
<a href="/#/#faq-2">Question 2</a>
<a href="/#/#faq-3">Question 3</a>

R
Reimund

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

M
MrFlo

我使用 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"]?此外,如果没有明确的 jQuery 包含,则处理程序必须与 element.on('click', function (e) {..}) 绑定。
E
Edmar Miyake

您可以尝试使用 anchorScroll

Example

所以控制器将是:

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>

S
Stoyan Kenderov

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

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

从 Angular 应用模块的 .run() 部分注册一个 document.onload 事件处理程序。在处理程序中,通过执行一些字符串操作找出原始的锚标记应该是什么。用剥离的锚标记覆盖 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) { // 确保这个标签存在于文档中。 location.hash = potentialAnchor; $anchorScroll(); } } });


B
Brian

我不是 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>

A
Aakash

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

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

凉亭bower install --save angular-scroll

npmnpm install --save angular-scroll

Minfied 版本 - 只有 9kb

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

滚动间谍 - 是的

文档 - 优秀

演示 - http://oblador.github.io/angular-scroll/

希望这可以帮助。


T
T J

请参阅 https://code.angularjs.org/1.4.10/docs/api/ngRoute/provider/$routeProvider

[reloadOnSearch=true] - {boolean=} - 当只有 $location.search() 或 $location.hash() 改变时重新加载路由。

将其设置为 false 对我来说没有上述所有内容就可以了。


T
ThatMSG

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

S
Suhail Ahmed

试试这个将解决锚问题。

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

P
Praveen M P

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

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

将此代码放在您的控制器上。


J
Jackie

在我看来@slugslog 有它,但我会改变一件事。我会改用替换,所以你不必重新设置它。

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

Docs 搜索“替换方法”


w
windmaomao

上面的解决方案都不适合我,但我只是尝试了这个,它奏效了,

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

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


K
Kulbhushan Chaskar

有时在 angularjs 应用程序哈希导航中不起作用,并且引导 jquery javascript 库广泛使用这种类型的导航,使其工作添加 target="_self" 到锚标记。例如<a data-toggle="tab" href="#id_of_div_to_navigate" target="_self">


T
T J

我正在使用 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 进行任何更改。