Как обрабатывать привязку хеш-ссылок в AngularJS

298

Кто-нибудь из вас знает, как хорошо обрабатывать привязку привязки привязки в 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 +, поэтому я боюсь, что это не принятое решение:/

  • 0
    Я думаю, что angular предлагает вместо этого использовать ng-href="" .
  • 9
    Я думаю, что ng-href применим только в том случае, если URL содержит динамические данные, которые необходимо привязать к ng-модели. Мне интересно, если вы назначите hashPrefix для locationProvider, если он будет игнорировать ссылки на идентификаторы: docs.angularjs.org/guide/dev_guide.services.$location
Показать ещё 4 комментария
Теги:
anchor
hashtag

26 ответов

376
Лучший ответ

Вы ищете $anchorScroll().

Здесь (дерьмовая) документация.

И вот источник.

В основном вы просто вводите его и вызываете его в своем контроллере, и он будет прокручивать вас до любого элемента с идентификатором, найденным в $location.hash()

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>

Здесь показан плункер.

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>
  • 5
    Проблема может возникнуть, когда вы добавляете маршрутизацию: если добавить ngView, каждое изменение хеша URL будет вызывать перезагрузку маршрута ... В вашем примере нет маршрутизации, и URL не отражает текущий элемент ... Но спасибо за указание на $ anchorScroll
  • 0
    Я обновил свой ответ, чтобы включить пример с маршрутизацией.
Показать ещё 18 комментариев
168

в моем случае я заметил, что логика маршрутизации была нажата, если я изменил хэш местоположения. следующий трюк работал.

$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);
};
  • 5
    Блестящее спасибо за это, логика маршрутизации абсолютно отказалась вести себя даже при использовании решения @ blesh .run (...), и это сортирует его.
  • 8
    Ваш трюк с "спасением старого хэша" был абсолютным спасителем. Это предотвращает перезагрузку страницы, сохраняя маршрут чистым. Отличная идея!
Показать ещё 18 комментариев
45

Нет необходимости изменять какую-либо маршрутизацию или что-то еще, просто нужно использовать 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 в html:

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

Нет необходимости использовать ## как указано/упомянуто в комментариях; -)

  • 1
    Это не сработало для меня, но решение здесь сделало: stackoverflow.com/a/19367249/633107
  • 6
    Спасибо за это решение. Но target = "_ self" достаточно. Нет необходимости удваивать #
Показать ещё 7 комментариев
42
<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>
  • 0
    Потрясающие! Безусловно самое простое решение, но есть идеи, как сделать ссылку на якорь на отдельной странице? (т.е. / продукты # книги)
  • 0
    Я думаю, что это то же самое, что решение (/ products ## books) в AngularJS
Показать ещё 7 комментариев
19

Если вы всегда знаете маршрут, вы можете просто добавить якорь следующим образом:

href="#/route#anchorID

где route - текущий маршрут angular, а anchorID соответствует <a id="anchorID"> где-то на странице

  • 1
    Это вызывает нормальное изменение маршрута AngularJS и поэтому не рекомендуется. В моем случае это было очень наглядно, поскольку видео YouTube на странице часто задаваемых вопросов / справки перезагружалось.
  • 9
    @ RobinWassén-Andersson, указав reloadOnSearch: false для этого маршрута в конфигурации маршрутов, angular не будет вызывать изменение маршрута и просто прокрутит до идентификатора. Я бы сказал, что в сочетании с полным маршрутом, указанным в теге a , это самое простое и простое решение.
Показать ещё 1 комментарий
14

Это было мое решение, используя директиву, которая выглядит больше Angular -y, потому что мы имеем дело с DOM:

Plnkr здесь

github

CODE

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-коммутатору, и обновления обновили бы приложение.

  • 0
    Мне нравится ваше директивное решение. Однако, как бы вы это ни делали, вы хотели загрузить другую страницу и одновременно прокрутить до места привязки. Без angularjs это было бы href = "location # hash". Но ваша директива предотвращает перезагрузку страницы.
  • 0
    @CMCDragonkai с моей директивой, я не уверен, потому что я использую вызов $ anchorScroll, который, похоже, обрабатывает только прокрутку к элементу, находящемуся в данный момент на странице. Возможно, вам придется возиться с $ location или $ window, чтобы получить что-то, связанное с изменением страницы.
Показать ещё 2 комментария
12

$anchorScroll работает для этого, но есть гораздо лучший способ использовать его в более поздних версиях Angular.

Теперь $anchorScroll принимает хеш в качестве необязательного аргумента, поэтому вам не нужно вообще менять $location.hash. (документация)

Это лучшее решение, потому что оно вообще не влияет на маршрут. Я не мог заставить любое другое решение работать, потому что я использую ngRoute, и маршрут будет перезагружаться, как только я установлю $location.hash(id), прежде чем $anchorScroll сможет сделать свою магию.

Вот как его использовать... во-первых, в директиве или контроллере:

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

а затем в представлении:

<a href="" ng-click="scrollTo(id)">Text</a>

Кроме того, если вам нужно учесть фиксированный navbar (или другой пользовательский интерфейс), вы можете установить смещение для $anchorScroll как это (в функции запуска основного модуля):

.run(function ($anchorScroll) {
   //this will make anchorScroll scroll to the div minus 50px
   $anchorScroll.yOffset = 50;
});
  • 0
    Благодарю. Как бы вы реализовали свою стратегию для хеш-ссылки в сочетании с изменением маршрута? Пример: щелкните этот элемент навигации, чтобы открыть другое представление и прокрутить вниз до определенного id в этом представлении.
  • 0
    Извините за приставание .. Если бы у вас была возможность, вы могли бы взглянуть на мой вопрос о стеке? Я чувствую, что ваш ответ здесь так близко подошел ко мне, но я просто не могу его реализовать: stackoverflow.com/questions/41494330/… . Я тоже использую ngRoute и более новую версию Angular.
Показать ещё 2 комментария
5

Это старый пост, но я долгое время изучал различные решения, поэтому хотел поделиться еще одним простым. Просто добавив target="_self" в тег <a>, исправил его для меня. Ссылка работает и возвращает меня в нужное место на странице.

Тем не менее, Angular все еще вводит какую-то странность С# в URL-адресе, поэтому вы можете столкнуться с трудностями с помощью кнопки "Назад" для навигации и после этого этого метода.

5

Я обошел это в логике маршрутов для своего приложения.

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;
      }
    });
}
  • 1
    Это не работает: Ошибка: [$ injector: modulerr] Не удалось создать экземпляр угла модуля из-за: Ошибка: недопустимый «обработчик» в when ()
5

Попробуйте установить префикс хеша для angular маршрутов $locationProvider.hashPrefix('!')

Полный пример:

angular.module('app', [])
  .config(['$routeProvider', '$locationProvider', 
    function($routeProvider, $locationProvider){
      $routeProvider.when( ... );
      $locationProvider.hashPrefix('!');
    }
  ])
  • 0
    Это не влияет на результат. Это было бы мило, хотя.
  • 2
    Уверены ли вы. Почему это не работает? Если префикс хеша равен!, То хеш-маршрутизация должна быть #! Page. Поэтому AngularJS должен определять, когда это просто #hash, он должен автоматически привязывать прокрутку и работать как с URL-адресами режима HTML5, так и с URL-адресами режима хеширования.
4

Это может быть новый атрибут для ngView, но мне удалось привязать хэш-ссылки для работы с angular-route с помощью атрибута ngView autoscroll и "double-hashes".

ngView (см. 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>
3

Я мог бы сделать это так:

<li>
<a href="#/#about">About</a>
</li>
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>
  • 0
    Не нужно ли указывать атрибут типа scroll-to="yourid" и scroll-to="yourid" имя директиве scrollTo (и обращаться к атрибуту как attrs["scrollTo"] ? Кроме того, без явного включения jQuery обработчик должен быть связан с element.on('click', function (e) {..}) .
2

Вот как-то грязное обходное решение, создавая настраиваемую директиву, которая будет прокручиваться до указанного элемента (с жестко закодированным "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

  • 0
    Это действительно работает, но это, как вы сказали, грязно. Очень грязный. Давайте посмотрим, может ли кто-то еще придумать более красивое решение, или мне придется пойти с этим.
  • 0
    Angular уже имеет функцию scrollTo, встроенную через $anchorScroll , см. Мой ответ.
Показать ещё 1 комментарий
1

Если вы не любите использовать 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;
    };
}])
1

Получить прокрутку. Он также поддерживает Анимированную/Гладкую прокрутку в качестве дополнительной функции. Подробная информация для Angular Библиотека прокрутки:

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

Бауэр: bower install --save angular-scroll

npm: npm install --save angular-scroll

Minfied version - только 9kb

Плавная прокрутка (анимированная прокрутка) - да

Scroll Spy - да

Документация - отлично

Демо - http://oblador.github.io/angular-scroll/

Надеюсь, что это поможет.

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

Я не уверен на 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>

В заключение

Включение имени страницы перед тем, как якорь помогло. Дайте мне знать о ваших мыслях.

Даунсайд

Это приведет к повторному отображению страницы, а затем прокрутку к якорю.

UPDATE

Лучше всего добавить следующее:

<a href="#tab1" onclick="return false;">First Part</a>
1

Вы можете попытаться использовать 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>

... и не секрет для идентификатора привязки:

<div id="foo">
  This is #foo
</div>
0

См. https://code.angularjs.org/1.4.10/docs/api/ngRoute/provider/ $routeProvider

[reloadOnSearch = true] - {boolean =} - перезагрузить маршрут, когда изменяются только $location.search() или $location.hash().

Установка этого значения в false сделала трюк без всего вышеперечисленного для меня.

0

Когда-то в angularjs приложение hash navigation не работает, а javascript-библиотеки bootstrap javascript широко используют этот тип навигации, чтобы заставить его работать, добавляя target="_self" к тегу привязки. например <a data-toggle="tab" href="#id_of_div_to_navigate" target="_self">

0

Я использую 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

Ни одно из вышеперечисленных решений не работает для меня, но я просто попробовал это, и это сработало,

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

Итак, я понял, что мне нужно уведомить страницу, чтобы начать с индексной страницы, а затем использовать традиционный якорь.

0

В моем уме @slugslog имел это, но я бы изменил одно. Я бы использовал вместо этого замену, поэтому вам не нужно ее возвращать.

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

Docs Поиск "Заменить метод"

0

При изменении маршрута он будет прокручиваться до верхней части страницы.

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

поместите этот код на свой контроллер.

0

Я пытался сделать мое приложение Angular прокруткой к загрузке якоря opon и столкнулось с правилами перезаписи URL-адреса $routeProvider.

После долгих экспериментов я остановился на этом:

  • зарегистрировать обработчик события document.onload из раздела .run() модуль приложения Angular.
  • в обработчике узнать, что оригинал Предполагается, что якорный тег должен выполняться строковыми операциями.
  • переопределить 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();
			}
		}	 
 });

Ещё вопросы

Сообщество Overcoder
Наверх
Меню