javascript - top - Cómo manejar el enlace de hash de anclaje en AngularJS
ng-href angularjs (27)
¿Alguno de ustedes sabe manejar bien el enlace de hash de ancla 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 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 que coincida con " / faq /: chapter " y en el controlador correspondiente verifique $routeParams.chapter
después de un elemento correspondiente y luego use jQuery para desplazarse hacia abajo.
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?
Edición: Cambiar a html5Mode debería resolver mis problemas, pero de todos modos tenemos que admitir IE8 +, así que me temo que no es una solución aceptada: /
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();
}
}
});
Consulte https://code.angularjs.org/1.4.10/docs/api/ngRoute/provider/ $ routeProvider
[reloadOnSearch = true] - {boolean =} - vuelve a cargar la ruta cuando solo cambian $ location.search () o $ location.hash ().
Poner esto en falso hizo el truco sin todo lo anterior para mí.
En algún momento de la aplicación hash angularjs, la navegación no funciona y las bibliotecas javascript jquery de bootstrap hacen un uso extensivo de este tipo de navegación, para que funcione agregue target="_self"
a la etiqueta de anclaje. por ejemplo, <a data-toggle="tab" href="#id_of_div_to_navigate" target="_self">
En el cambio de ruta, se desplazará a la parte superior de la página.
$scope.$on(''$routeChangeSuccess'', function () {
window.scrollTo(0, 0);
});
Pon este código en tu controlador.
En mi caso, noté que la lógica de enrutamiento se activaba si modificaba $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);
};
En mi mente, @slugslog lo tenía, pero cambiaría una cosa. Yo usaría reemplazar en su lugar para que no tenga que restablecerlo.
$scope.scrollTo = function(id) {
var old = $location.hash();
$location.hash(id).replace();
$anchorScroll();
};
Búsqueda de Docs para "Reemplazar método"
Estás buscando $anchorScroll()
.
Básicamente, simplemente lo inyectas y lo llamas en tu controlador, y te 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 plunker para demostrar
EDITAR: para usar esto con enrutamiento.
Configure su ruta angular como de costumbre, luego solo agregue el siguiente código.
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 su enlace se vería así:
<a href="#/test?scrollTo=foo">Test/Foo</a>
Aquí hay un Plunker que muestra el desplazamiento con enrutamiento y $ 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 su enlace se vería así:
<a href="#/test#foo">Test/Foo</a>
Esta fue mi solución usando una directiva que parece más angular-y porque estamos tratando con el DOM:
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>
Utilicé el servicio $ anchorScroll. Para contrarrestar la actualización de la página que acompaña al cambio de hash, continué y cancelé el evento locationChangeStart. Esto me funcionó porque tenía una página de ayuda conectada a un interruptor ng y las actualizaciones romperían la aplicación.
Estaba tratando de hacer que mi aplicación Angular se desplazara a un ancla que se estaba cargando y encontré las reglas de reescritura de URL de $ routeProvider
Después de una larga experimentación me decidí por esto:
- registre un controlador de eventos document.onload desde la sección .run () del módulo de la aplicación Angular.
- en el controlador, averigüe qué se suponía que tenía la etiqueta de anclaje original realizando algunas operaciones de cadena.
- sobrescribir location.hash con la etiqueta de ancla reducida (lo que hace que $ routeProvider la sobrescriba de inmediato con su regla "# /". Pero eso está bien, porque Angular ahora está sincronizado con lo que está sucediendo en la 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();
}
}
});
Este es un post antiguo, pero pasé mucho tiempo investigando varias soluciones, así que quería compartir una más simple. Solo agregando target="_self"
a la etiqueta <a>
arreglé para mí El enlace funciona y me lleva a la ubicación correcta en la página.
Sin embargo, Angular aún inyecta algunas rarezas con el # en la URL, por lo que puede tener problemas al usar el botón de retroceso para la navegación y tal después de usar este método.
Este es un tipo de solución sucia al crear una directiva personalizada que se desplazará al elemento especificado (con "preguntas frecuentes")
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);
}
}
};
});
Este puede ser un nuevo atributo para ngView, pero he podido conseguir que los enlaces hash de anclaje funcionen con angular-route
utilizando el ngView autoscroll
y "doble hashes".
ngView (ver desplazamiento automático)
(El siguiente código fue usado con la correa angular)
<!-- 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>
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.
Intente establecer un prefijo hash para rutas angulares $locationProvider.hashPrefix(''!'')
Ejemplo completo:
angular.module(''app'', [])
.config([''$routeProvider'', ''$locationProvider'',
function($routeProvider, $locationProvider){
$routeProvider.when( ... );
$locationProvider.hashPrefix(''!'');
}
])
Logré evitar 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;
}
});
}
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>
Ninguna de las soluciones anteriores funciona para mí, pero solo probé esto y funcionó,
<a href="#/#faq-1">Question 1</a>
Entonces me di cuenta de que debía notificar a la página para comenzar con la página de índice y luego usar el ancla tradicional.
No estoy 100% seguro de 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 que tienes la siguiente ruta:
yourApp.config([''$routeProvider'',
function($routeProvider) {
$routeProvider.
when(''/about'', {
templateUrl: ''about.html'',
controller: ''AboutCtrl''
}).
otherwise({
redirectTo: ''/''
});
}
]);
Ahora, en tu 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 hiciera el truco por mí. Déjame saber acerca de tus pensamientos.
Abajo
Esto volverá a representar la página y luego se desplazará hasta el ancla.
ACTUALIZAR
Una mejor manera es agregar lo siguiente:
<a href="#tab1" onclick="return false;">First Part</a>
No hay necesidad de cambiar ninguna ruta o cualquier otra cosa, solo necesita 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 es necesario utilizar ## como se indica / se menciona en los comentarios ;-)
Obtenga su función de desplazamiento fácilmente. También es compatible con el desplazamiento animado / suave como una característica adicional. Detalles para la biblioteca de Angular Scroll :
Github - https://github.com/oblador/angular-scroll
Bower : bower install --save angular-scroll
npm : npm install --save angular-scroll
Versión reducida - solo 9kb
Desplazamiento suave (desplazamiento animado) - sí
Scry Spy - si
Documentación - excelente
Demostración - http://oblador.github.io/angular-scroll/
Espero que esto ayude.
Puedes intentar usar anchorScroll .
Entonces 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 ningún secreto para la identificación del ancla:
<div id="foo">
This is #foo
</div>
Si no te gusta usar ng-click
aquí hay una solución alternativa. Utiliza un filter
para generar la url correcta en función del estado actual. Mi ejemplo utiliza ui.router .
El beneficio es que el usuario verá a dónde se dirige el enlace.
<a href="{{ ''my-element-id'' | anchor }}">My element</a>
El filtro:
.filter(''anchor'', [''$state'', function($state) {
return function(id) {
return ''/#'' + $state.current.url + ''#'' + id;
};
}])
Si siempre conoce la ruta, simplemente puede agregar el ancla de esta manera:
href="#/route#anchorID
donde route
es la ruta angular actual y el anchorID
coincide con un <a id="anchorID">
algún lugar de la página
Yo podría hacer esto así
<li>
<a href="#/#about">About</a>
</li>
$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. ( $anchorScroll() )
Esta es la mejor solución porque no afecta la ruta en absoluto. No pude hacer funcionar ninguna de las otras soluciones porque estoy usando ngRoute y la ruta se volvería a cargar tan pronto como establezca $location.hash(id)
, antes de que $anchorScroll
pueda hacer su magia.
Aquí está cómo usarlo ... primero, en la directiva o controlador:
$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 IU), puede configurar el desplazamiento para $ anchorScroll de esta manera (en la función de ejecución del módulo principal):
.run(function ($anchorScroll) {
//this will make anchorScroll scroll to the div minus 50px
$anchorScroll.yOffset = 50;
});
<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>
<a href="/#/#faq-1">Question 1</a>
<a href="/#/#faq-2">Question 2</a>
<a href="/#/#faq-3">Question 3</a>