I'm tratando de obtener una navegación rápida para que funcione correctamente. Está flotando en el lateral. Cuando hacen clic en un enlace, los lleva a ese ID en la página. I'm siguiendo esta guía de Treehouse. Esto es lo que tengo para el desplazamiento:
$("#quickNav a").click(function(){
var quickNavId = $(this).attr("href");
$("html, body").animate({scrollTop: $(location).offset().top}, "slow");
return false;
});
Inicialmente lo coloqué antes del </body>
. Pero me parece estar corriendo en una condición de carrera en la que se dispara antes de la quickNav compilado (tiene un ng-ocultar
colocado en él, no estoy seguro de si eso es la causa - pero es dentro del DOM).
Si ejecuto ese bloque de código en la consola, entonces el desplazamiento funciona como se esperaba.
Me imaginé que sería más eficaz para mover esto en el controlador - o más probablemente dentro de una directiva. Pero no estoy teniendo suerte logrando eso. **¿Cómo puedo hacer que este bloque de código funcione con AngularJS?
Aquí hay una directiva simple que se desplazará a un elemento al hacer clic:
myApp.directive('scrollOnClick', function() {
return {
restrict: 'A',
link: function(scope, $elm) {
$elm.on('click', function() {
$("body").animate({scrollTop: $elm.offset().top}, "slow");
});
}
}
});
Demo: http://plnkr.co/edit/yz1EHB8ad3C59N6PzdCD?p=preview
Para obtener ayuda en la creación de directivas, echa un vistazo a los vídeos en http://egghead.io, a partir de la #10 "primera directiva".
editar: Para hacer que se desplace a un elemento específico especificado por un href, simplemente marque attrs.href
.
myApp.directive('scrollOnClick', function() {
return {
restrict: 'A',
link: function(scope, $elm, attrs) {
var idToScroll = attrs.href;
$elm.on('click', function() {
var $target;
if (idToScroll) {
$target = $(idToScroll);
} else {
$target = $elm;
}
$("body").animate({scrollTop: $target.offset().top}, "slow");
});
}
}
});
Entonces podrías usarlo así <div scroll-on-click></div>
para desplazarse al elemento pulsado. O <a scroll-on-click href="#element-id"></div>
para desplazarse al elemento con el id.
Esta es una directiva mejor en caso de que quiera utilizarla:
puede desplazarse a cualquier elemento de la página:
.directive('scrollToItem', function() {
return {
restrict: 'A',
scope: {
scrollTo: "@"
},
link: function(scope, $elm,attr) {
$elm.on('click', function() {
$('html,body').animate({scrollTop: $(scope.scrollTo).offset().top }, "slow");
});
}
}})
Uso (por ejemplo, haga clic en div 'back-to-top' se desplazará a id scroll-top):
<a id="top-scroll" name="top"></a>
<div class="back-to-top" scroll-to-item scroll-to="#top-scroll">
También es compatible con Chrome, Firefox, Safari e IE debido al elemento html, body.
Gracias Andy por el ejemplo, esto fue muy útil. Terminé implementando una estrategia ligeramente diferente ya que estoy desarrollando un scroll de una sola página y no quería que Angular refrescara al usar la URL hashbang. También quiero preservar la acción atrás/adelante del navegador.
En lugar de usar la directiva y el hash, estoy usando un $scope.$watch en el $location.search, y obteniendo el objetivo desde ahí. Esto da una bonita y limpia etiqueta de anclaje
<a ng-href="#/?scroll=miElemento">Mi elemento</a>
.
Encadené el código del reloj a la declaración de mi módulo en app.js de la siguiente manera:
.run(function($location, $rootScope) {
$rootScope.$watch(function() { return $location.search() }, function(search) {
var scrollPos = 0;
if (search.hasOwnProperty('scroll')) {
var $target = $('#' + search.scroll);
scrollPos = $target.offset().top;
}
$("body,html").animate({scrollTop: scrollPos}, "slow");
});
})
La advertencia con el código anterior es que si se accede por URL directamente desde una ruta diferente, el DOM puede no ser cargado a tiempo para jQuery's $target.offset() llamada. La solución es anidar este código dentro de un watcher $viewContentLoaded. El código final es algo parecido a esto:
.run(function($location, $rootScope) {
$rootScope.$on('$viewContentLoaded', function() {
$rootScope.$watch(function() { return $location.search() }, function(search) {
var scrollPos = 0
if (search.hasOwnProperty('scroll')) {
var $target = $('#' + search.scroll);
var scrollPos = $target.offset().top;
}
$("body,html").animate({scrollTop: scrollPos}, "slow");
});
});
})
Probado con Chrome y FF