Cómo manejar el enlace de hash de anclaje en AngularJS


¿Alguno de ustedes sabe cómo manejar muy bien el enlace de hash de anclaje en AngularJS?

Tengo el siguiente marcado para una simple página de preguntas frecuentes

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

Al hacer clic en cualquiera de los enlaces anteriores AngularJS me intercepta y me dirige a una página completamente diferente (en mi caso, una página 404, ya que no hay rutas que coincidan con los enlaces.)

Mi primer pensamiento fue crear una ruta coincidente "/faq/: chapter " y en el control correspondiente $routeParams.chapter después de una coincidencia elemento y luego utilizar jQuery para desplazarse hacia abajo a él.

Pero luego AngularJS me caga de nuevo y simplemente se desplaza a la parte superior de la página de todos modos.

Entonces, ¿alguien aquí hizo algo similar en el pasado y conoce una buena solución?

Editar: Cambiar a html5Mode debería resolver mis problemas, pero tenemos que soportar IE8 + de todos modos, así que me temo que no es una solución aceptada: /

Author: Sunil Garg, 2013-02-05

27 answers

Estás buscando $anchorScroll().

Aquí está la documentación (de mierda).

Y aquí está la fuente.

Básicamente solo lo inyecta y lo llama en su controlador, y se desplazará a cualquier elemento con el id que se encuentra en $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>

Aquí hay un émbolo para demostrar

EDITAR: para usar esto con enrutamiento

Configure su enrutamiento angular como de costumbre, luego simplemente agregue lo siguiente codificar.

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

Y tu enlace se vería así:

<a href="#/test?scrollTo=foo">Test/Foo</a>

Aquí hay un Émbolo que demuestra el desplazamiento con enrutamiento y anchor anchorScroll

Y aún más simple:

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

Y tu enlace se vería así:

<a href="#/test#foo">Test/Foo</a>
 369
Author: Ben Lesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-21 22:15:33

En mi caso, noté que la lógica de enrutamiento se activaba si modificaba el $location.hash(). El siguiente truco funcionó..

$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);
};
 165
Author: slugslog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-15 22:47:27

No hay necesidad de cambiar ningún enrutamiento o cualquier otra cosa solo hay que usar target="_self" al crear los enlaces

Ejemplo:

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

Y usa el atributo id en tus elementos html como este:

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

No hay necesidad de usar ## como señalado / mencionado en los comentarios; -)

 51
Author: Mauricio Gracia Gutierrez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-17 13:03:01
<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>
 41
Author: lincolnge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-02 07:34:41

Si siempre conoce la ruta, simplemente puede agregar el ancla de esta manera:

href="#/route#anchorID

Donde route es la ruta angular actual y anchorID coincide con un <a id="anchorID"> en algún lugar de la página

 19
Author: cab1113,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-14 18:57:10

Esta fue mi solución usando una directiva que parece más Angular-y porque estamos tratando con el DOM:

Plnkr aquí

Github

CÓDIGO

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>

Usé el servicio anchor anchorScroll. Para contrarrestar la actualización de página que va junto con el cambio de hash, seguí adelante y cancelé el evento locationChangeStart. Esto funcionó para mí porque tenía una página de ayuda conectada a un ng-switch y las actualizaciones esencialmente romper la aplicación.

 14
Author: KhalilRavanna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-28 22:52:44

$anchorScroll funciona para esto, pero hay una manera mucho mejor de usarlo en versiones más recientes de Angular.

Ahora, $anchorScroll acepta el hash como un argumento opcional, por lo que no tiene que cambiar $location.hash en absoluto. (documentación )

Esta es la mejor solución porque no afecta a la ruta en absoluto. No pude conseguir que ninguna de las otras soluciones funcionara porque estoy usando ngRoute y la ruta se recargaría tan pronto como configurara $location.hash(id), antes de que $anchorScroll pudiera hacer su magia.

Aquí es cómo usarlo... en primer lugar, en la directiva o responsable del tratamiento:

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

Y luego en la vista:

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

Además, si necesita tener en cuenta una barra de navegación fija (u otra interfaz de usuario), puede establecer el desplazamiento para anchor anchorScroll de esta manera (en la función run del módulo principal):

.run(function ($anchorScroll) {
   //this will make anchorScroll scroll to the div minus 50px
   $anchorScroll.yOffset = 50;
});
 13
Author: Rebecca,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-21 11:35:10

Intenta establecer un prefijo hash para las rutas angulares $locationProvider.hashPrefix('!')

Ejemplo completo:

angular.module('app', [])
  .config(['$routeProvider', '$locationProvider', 
    function($routeProvider, $locationProvider){
      $routeProvider.when( ... );
      $locationProvider.hashPrefix('!');
    }
  ])
 5
Author: Maxim Grach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-15 08:28:37

Tengo alrededor de esto en la lógica de ruta para mi aplicación.

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;
      }
    });
}
 5
Author: nicksanta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-11 13:06:08

Este es un post antiguo, pero pasé mucho tiempo investigando varias soluciones, así que quería compartir una más simple. Simplemente agregando target="_self" a la etiqueta <a> lo arreglé para mí. El enlace funciona y me lleva a la ubicación adecuada en la página.

Sin embargo, Angular todavía inyecta algo raro con el # en la URL, por lo que puede tener problemas con el botón atrás para la navegación y tal después de usar este método.

 5
Author: Benjamin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-18 12:33:19

Este puede ser un nuevo atributo para ngView, pero he sido capaz de obtener enlaces hash de anclaje para trabajar con angular-route usando el atributo ngView autoscroll y 'doble-hashes'.

NgView (véase autoscroll)

(El siguiente código se utilizó con 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>
 4
Author: michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-24 14:48:29

Podría hacer esto así:

<li>
<a href="#/#about">About</a>
</li>
 3
Author: Niru,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-21 09:09:05

Aquí hay una especie de solución sucia al crear una directiva personalizada que se desplaza al elemento especificado (con "preguntas frecuentes"codificadas)

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

 2
Author: Valentyn Shybanov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-05 17:04:56
<a href="/#/#faq-1">Question 1</a>
<a href="/#/#faq-2">Question 2</a>
<a href="/#/#faq-3">Question 3</a>
 2
Author: felipe_dmz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-19 20:32:43

Mi solución con ng-route fue esta simple directiva:

   app.directive('scrollto',
       function ($anchorScroll,$location) {
            return {
                link: function (scope, element, attrs) {
                    element.click(function (e) {
                        e.preventDefault();
                        $location.hash(attrs["scrollto"]);
                        $anchorScroll();
                    });
                }
            };
    })

El html se ve como:

<a href="" scrollTo="yourid">link</a>
 2
Author: MrFlo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-15 20:32:26

Puedes intentar usar anchorScroll.

Ejemplo

Así que el controlador sería:

app.controller('MainCtrl', function($scope, $location, $anchorScroll, $routeParams) {
  $scope.scrollTo = function(id) {
     $location.hash(id);
     $anchorScroll();
  }
});

Y la vista:

<a href="" ng-click="scrollTo('foo')">Scroll to #foo</a>

...y no hay secreto para el anchor id:

<div id="foo">
  This is #foo
</div>
 1
Author: Edmar Miyake,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-24 02:26:47

Estaba tratando de hacer que mi aplicación Angular se desplace a una carga opon de anclaje y se encontró con las reglas de reescritura de URL de rout routeProvider.

Después de una larga experimentación me decidí por esto:

  1. registrar un documento.onload controlador de eventos desde el .ejecutar() sección de el módulo Angular app.
  2. en el controlador averiguar lo que el original tiene la etiqueta de anclaje se suponía que era haciendo algunas operaciones de cadena.
  3. anular ubicación.hash con la etiqueta de ancla despojada (que causas $routeProvider inmediatamente reescribirla de nuevo con "#/" regla. Pero eso está bien, porque Angular ahora está en sincronización con qué está pasando en la URL 4) llame a anchor 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();
			}
		}	 
 });
 1
Author: Stoyan Kenderov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-22 02:59:39

No estoy 100% seguro si esto funciona todo el tiempo, pero en mi aplicación esto me da el comportamiento esperado.

Digamos que estás en la página ABOUT y tienes la siguiente ruta:

yourApp.config(['$routeProvider', 
    function($routeProvider) {
        $routeProvider.
            when('/about', {
                templateUrl: 'about.html',
                controller: 'AboutCtrl'
            }).
            otherwise({
                redirectTo: '/'
            });
        }
]);

Ahora, en 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>

En conclusión

Incluyendo el nombre de la página antes de que el ancla hizo el truco para mí. Hazme saber sobre tus pensamientos.

Desventaja

Esto re-renderizará la página y luego se desplazará a la ancla.

UPDATE

Una mejor manera es agregar lo siguiente:

<a href="#tab1" onclick="return false;">First Part</a>
 1
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-21 18:29:24

Si no te gusta usar ng-click aquí hay una solución alternativa. Utiliza un filter para generar la url correcta basada en el estado actual. Mi ejemplo usa ui.router .

El beneficio es que el usuario verá dónde va el enlace en hover.

<a href="{{ 'my-element-id' | anchor }}">My element</a>

El filtro:

.filter('anchor', ['$state', function($state) {
    return function(id) {
        return '/#' + $state.current.url + '#' + id;
    };
}])
 1
Author: Reimund,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-06 13:11:03

Obtenga su función de desplazamiento fácilmente. También soporta Desplazamiento animado/suave como una característica adicional. Detalles para Angular Scroll biblioteca:

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

Bower: bower install --save angular-scroll

Mnp : npm install --save angular-scroll

Versión reducida - solo 9kb

Desplazamiento suave (desplazamiento animado) - sí

Scroll Spy - sí

Documentación - excelente

Demo - http://oblador.github.io/angular-scroll/

Espero que esto ayude.

 1
Author: Akash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-12 09:02:43

Basado en @Stoyan se me ocurrió la siguiente solución:

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
Author: ThatMSG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-26 21:02:39

En cambio de ruta se desplazará a la parte superior de la página.

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

Ponga este código en su controlador.

 0
Author: Praveen M P,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-12 13:47:29

En mi mente @slugslog lo tenía, pero cambiaría una cosa. Usaría reemplazar en su lugar para que no tengas que retrasarlo.

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

Docs Buscar "Método de reemplazo"

 0
Author: Jackie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-04 19:51:00

Ninguna de las soluciones anteriores funciona para mí, pero acabo de probar esto, y funcionó,

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

Así que me di cuenta de que necesito notificar a la página para comenzar con la página de índice y luego usar el ancla tradicional.

 0
Author: windmaomao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-21 17:57:33

En algún momento en la aplicación angularjs la navegación hash no funciona y bootstrap jquery las bibliotecas javascript hacen un uso extensivo de este tipo de navegación, para que funcione agrega target="_self" a la etiqueta de anclaje. por ejemplo, <a data-toggle="tab" href="#id_of_div_to_navigate" target="_self">

 0
Author: Kulbhushan Chaskar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-30 16:09:55

Véase https://code.angularjs.org/1.4.10/docs/api/ngRoute/provider / rout routeProvider

[reloadOnSearch=true] - {boolean=} - recargue la ruta cuando solo $location.search () o location location.hash() cambia.

Establecer esto en falso hizo el truco sin todo lo anterior para mí.

 0
Author: geekdenz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-21 11:33:30

Estoy usando AngularJS 1.3.15 y parece que no tengo que hacer nada especial.

Https://code.angularjs.org/1.3.15/docs/api/ng/provider/$anchorScrollProvider

Por lo tanto, lo siguiente funciona para mí en mi html:

<ul>
  <li ng-repeat="page in pages"><a ng-href="#{{'id-'+id}}">{{id}}</a>
  </li>
</ul>
<div ng-attr-id="{{'id-'+id}}" </div>

No tuve que hacer ningún cambio en mi controlador o JavaScript en absoluto.

 0
Author: Digant C Kasundra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-21 11:36:58