关于javascript:如何在AngularJS中处理锚式哈希链接

How to handle anchor hash linking in AngularJS

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

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

1
2
3
4
5
6
7
Question 1
Question 2
Question 3

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

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

我的第一个想法是创建一个路由匹配"/faq/:chapter",在相应的控制器中,在匹配元素之后检查$routeParams.chapter,然后使用jquery向下滚动到它。

但是安古拉吉斯又一次对我大惊小怪,而且还是会滚动到页面的顶部。

所以,这里有人做过类似的事情,并且知道一个很好的解决方法吗?

编辑:切换到HTML5模式可以解决我的问题,但我们还是需要支持IE8+,所以我担心这不是一个公认的解决方案:/


你在找$anchorScroll()

这是(蹩脚的)文档。

这是来源。

基本上,您只需注入它并在控制器中调用它,它就会滚动到$location.hash()中找到的任何ID元素。

1
2
3
4
5
6
7
8
9
10
app.controller('TestCtrl', function($scope, $location, $anchorScroll) {
   $scope.scrollTo = function(id) {
      $location.hash(id);
      $anchorScroll();
   }
});

Foo

Here you are

这里有一个抢劫者来演示

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

像往常一样设置角度布线,然后添加以下代码。

1
2
3
4
5
6
7
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();  
  });
});

您的链接如下所示:

1
Test/Foo

这是一个展示滚动与路由和$anchorsoll

更简单的是:

1
2
3
4
5
6
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();  
  });
});

您的链接如下所示:

1
Test/Foo


在我的例子中,我注意到,如果我修改了$location.hash(),路由逻辑就开始起作用了。以下技巧奏效了。

1
2
3
4
5
6
7
$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"的内容。

例子:

1
2
3
Question 1
Question 2
Question 3

在HTML元素中使用id属性,如下所示:

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

无需使用评论中指出的;-)


1
2
3
4
5
6
7
Question 1
Question 2
Question 3

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


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

1
href="#/route#anchorID

where route is the current angular route and anchorID matches an somewhere on the page


这是我的解决方案,使用了一个角度更大的指令y,因为我们要处理的是dom:

这里的PLNKR

github

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<ul>

 
<li>
Section 1
</li>

 
<li>
Section 2
</li>


</ul>


<h1 id="section1">Hi, I'm section 1
<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
<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>

我使用了$anchorsoll服务。为了抵消与哈希更改一起进行的页面刷新,我继续并取消了locationChangeStart事件。这对我很有效,因为我有一个连接到ng开关的帮助页面,刷新会破坏应用程序。


$anchorScroll适用于此,但有更好的方法可以在最近版本的Angular中使用它。

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

这是最好的解决方案,因为它根本不影响路线。我无法使用其他任何解决方案,因为我正在使用ngroute,而且在$anchorScroll发挥其魔力之前,只要设置$location.hash(id),路由就会重新加载。

以下是如何使用它…首先,在指令或控制器中:

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

然后在视图中:

1
Text

此外,如果您需要说明固定导航栏(或其他用户界面),您可以这样设置$anchorsroll的偏移量(在主模块的运行函数中):

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


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

完整例子:

1
2
3
4
5
6
7
angular.module('app', [])
  .config(['$routeProvider', '$locationProvider',
    function($routeProvider, $locationProvider){
      $routeProvider.when( ... );
      $locationProvider.hashPrefix('!');
    }
  ])


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
      }
    });
}


这是一篇旧文章,但我花了很长时间研究各种解决方案,所以我想再分享一篇简单的解决方案。只是把target="_self"添加到标签中,为我修复了它。链接工作,并带我到页面上的正确位置。

但是,Angular仍然会给URL中的注入一些奇怪的东西,因此在使用back按钮进行导航时可能会遇到麻烦,在使用此方法之后也会遇到类似的问题。


这可能是ngview的一个新属性,但我已经能够让它锚定散列链接使用ngView autoscroll属性和"双散列"与angular-route一起工作。

ngview(见自动滚动)

(以下代码与角带一起使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- use the autoscroll attribute to scroll to hash on $viewContentLoaded -->    


<!-- 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">HTML5
</li>

  <li data-target="#main-angular">Angular
</li>

  <li data-target="#main-karma">Karma
</li>


</ul>

我可以这样做:

1
2
3
4
5
<li>

About

</li>

下面是一种肮脏的解决方法,通过创建将滚动到指定元素的自定义指令(使用硬编码的"faq")。

1
2
3
4
5
6
7
8
9
10
11
12
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=预览


1
2
3
Question 1
Question 2
Question 3


我对ng route的解决方案是这个简单的指令:

1
2
3
4
5
6
7
8
9
10
11
12
   app.directive('scrollto',
       function ($anchorScroll,$location) {
            return {
                link: function (scope, element, attrs) {
                    element.click(function (e) {
                        e.preventDefault();
                        $location.hash(attrs["scrollto"]);
                        $anchorScroll();
                    });
                }
            };
    })

HTML看起来像:

1
link


我不完全确定这是否一直有效,但在我的应用程序中,这给了我预期的行为。

假设您在"关于"页面上,并且您有以下路径:

1
2
3
4
5
6
7
8
9
10
11
12
yourApp.config(['$routeProvider',
    function($routeProvider) {
        $routeProvider.
            when('/about', {
                templateUrl: 'about.html',
                controller: 'AboutCtrl'
            }).
            otherwise({
                redirectTo: '/'
            });
        }
]);

现在,用HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<ul>

   
<li>
First Part
</li>

   
<li>
Second Part
</li>

   
<li>
Third Part
</li>
                     

</ul>


1
2
3

总之

包括在主播给我做这个把戏之前的页面名称。告诉我你的想法。

下降趋势

这将重新呈现页面,然后滚动到定位点。

更新

更好的方法是添加以下内容:

1
First Part

我试图让我的Angular应用程序滚动到一个锚opon加载,然后运行到$routeprovider的URL重写规则中。

经过长时间的试验,我决定:

  • 从的.run()部分注册document.onload事件处理程序角度应用程序模块。
  • 在处理程序中查找原始has anchor标记应该是通过执行一些字符串操作来实现的。
  • 用剥离的锚标记重写location.hash(其中使$routeProvider立即用它的"规则"。但那很好,因为角现在与URL 4)调用$AnchorScroll()。
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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();
                }
            }    
     });


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

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

    鲍尔:埃多克斯1〔9〕。

    净现值:npm install --save angular-scroll

    最小版本-仅9KB

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

    滚动间谍-是

    文档-优秀

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

    希望这有帮助。


    如果您不喜欢使用ng-click,这里有一个替代的解决方案。它使用filter根据当前状态生成正确的URL。我的示例使用ui.router。

    好处是用户可以看到链接在何处悬停。

    1
    My element

    过滤器:

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


    你可以试着用锚卷轴。

    例子

    所以控制器应该是:

    1
    2
    3
    4
    5
    6
    app.controller('MainCtrl', function($scope, $location, $anchorScroll, $routeParams) {
      $scope.scrollTo = function(id) {
         $location.hash(id);
         $anchorScroll();
      }
    });

    观点:

    1
    Scroll to #foo

    …锚ID没有秘密:

    1
      This is #foo

    基于@Stoyan,我提出了以下解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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
    Question 1

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


    在我的脑海里,@slugslog拥有它,但我会改变一件事。我会用替代品,这样你就不用把它放回去了。

    1
    2
    3
    4
    5
    $scope.scrollTo = function(id) {
        var old = $location.hash();
        $location.hash(id).replace();
        $anchorScroll();
    };

    文档搜索"替换方法"


    我用的是AngularJS 1.3.15,看起来我不需要做任何特殊的事情。

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

    因此,在我的HTML中,以下内容适用于我:

    1
    2
    3
    4
    5
    6
    7
    8
    <ul>

      <li ng-repeat="page in pages">{{id}}
     
    </li>


    </ul>

    我根本不需要对我的控制器或javascript做任何更改。


    在AngularJS应用程序中,有时散列导航不起作用,而引导jquery javascript库广泛使用这种类型的导航,为了使其起作用,将target="_self"添加到锚标记中。如


    请参阅https://code.angularjs.org/1.4.10/docs/api/ngroute/provider/$routeprovider

    [reloadOnSearch=true] - {boolean=} - reload route when only $location.search() or $location.hash() changes.

    把这个设为假对我来说没有以上所有的这些就做了这个骗局。


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

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

    把这个代码放到你的控制器上。