Переключить класс привязки к событию прокрутки окна
Когда пользователь прокручивает окно своего браузера ниже определенной точки, я перехожу к классу #page div.
То, что я сделал до сих пор, прекрасно работает:
http://jsfiddle.net/eTTZj/29/
<div ng-app="myApp" scroll id="page">
<header></header>
<section></section>
</div>
app = angular.module('myApp', []);
app.directive("scroll", function ($window) {
return function(scope, element, attrs) {
angular.element($window).bind("scroll", function() {
if (this.pageYOffset >= 100) {
element.addClass('min');
console.log('Scrolled below header.');
} else {
element.removeClass('min');
console.log('Header is in view.');
}
});
};
});
(когда они прокручивают свое окно под заголовком, 100px, класс переключается)
Хотя, поправьте меня, если я ошибаюсь, я считаю, что это не правильный способ сделать это с помощью Angular.
Вместо этого я предположил, что лучшим способом для этого было бы использование ng-класса и сохранение булевского значения в области. Что-то вроде этого:
<div ng-app="myApp" scroll id="page" ng-class="{min: boolChangeClass}">
<header></header>
<section></section>
</div>
app = angular.module('myApp', []);
app.directive("scroll", function ($window) {
return function(scope, element, attrs) {
angular.element($window).bind("scroll", function() {
if (this.pageYOffset >= 100) {
scope.boolChangeClass = true;
console.log('Scrolled below header.');
} else {
scope.boolChangeClass = false;
console.log('Header is in view.');
}
});
};
});
Хотя это не динамично, если я изменяю значение scope.boolChangeClass в обратном вызове прокрутки, тогда ng-класс не обновляется.
Итак, мой вопрос: как лучше всего переключить класС#page, используя AngularJS, когда пользователь прокручивается ниже определенной точки?
Ответы
Ответ 1
Спасибо Flek за ответ на мой вопрос в его комментарии:
http://jsfiddle.net/eTTZj/30/
<div ng-app="myApp" scroll id="page" ng-class="{min:boolChangeClass}">
<header></header>
<section></section>
</div>
app = angular.module('myApp', []);
app.directive("scroll", function ($window) {
return function(scope, element, attrs) {
angular.element($window).bind("scroll", function() {
if (this.pageYOffset >= 100) {
scope.boolChangeClass = true;
} else {
scope.boolChangeClass = false;
}
scope.$apply();
});
};
});
Ответ 2
Почему вы все предлагаете тяжелые операции? Я не понимаю, почему это не решение "angular":
.directive('changeClassOnScroll', function ($window) {
return {
restrict: 'A',
scope: {
offset: "@",
scrollClass: "@"
},
link: function(scope, element) {
angular.element($window).bind("scroll", function() {
if (this.pageYOffset >= parseInt(scope.offset)) {
element.addClass(scope.scrollClass);
} else {
element.removeClass(scope.scrollClass);
}
});
}
};
})
Итак, вы можете использовать его следующим образом:
<navbar change-class-on-scroll offset="500" scroll-class="you-have-scrolled-down"></navbar>
или
<div change-class-on-scroll offset="500" scroll-class="you-have-scrolled-down"></div>
Ответ 3
Это мое решение, это не так сложно и позволяет использовать его для нескольких разметки через простую директиву ng-класса.
Таким образом, вы можете выбрать класс и scrollPos для каждого случая.
Ваш App.js:
angular.module('myApp',[])
.controller('mainCtrl',function($window, $scope){
$scope.scrollPos = 0;
$window.onscroll = function(){
$scope.scrollPos = document.body.scrollTop || document.documentElement.scrollTop || 0;
$scope.$apply(); //or simply $scope.$digest();
};
});
Ваш index.html:
<html ng-app="myApp">
<head></head>
<body>
<section ng-controller="mainCtrl">
<p class="red" ng-class="{fix:scrollPos >= 100}">fix me when scroll is equals to 100</p>
<p class="blue" ng-class="{fix:scrollPos >= 150}">fix me when scroll is equals to 150</p>
</section>
</body>
</html>
working JSFiddle здесь
EDIT:
Поскольку $apply()
на самом деле вызывает $rootScope.$digest()
, вы можете используйте $scope.$digest()
вместо $scope.$apply()
для более высокая производительность в зависимости от контекста.
Короче говоря: $apply()
всегда будет работать, но вынуждает $digest
во всех областях, которые могут вызвать проблема с производительностью.
Ответ 4
Может быть, это может помочь:)
контроллер
$scope.scrollevent = function($e){
// Your code
}
HTML
<div scroll scroll-event="scrollevent">//scrollable content</div>
или
<body scroll scroll-event="scrollevent">//scrollable content</body>
Директива
.directive("scroll", function ($window) {
return {
scope: {
scrollEvent: '&'
},
link : function(scope, element, attrs) {
$("#"+attrs.id).scroll(function($e) { scope.scrollEvent != null ? scope.scrollEvent()($e) : null })
}
}
})
Ответ 5
Как насчет производительности?
- Всегда деблокировать события для сокращения вычислений
- Используйте
scope.applyAsync
для уменьшения общего количества циклов дайджест
function debounce(func, wait) {
var timeout;
return function () {
var context = this, args = arguments;
var later = function () {
timeout = null;
func.apply(context, args);
};
if (!timeout) func.apply(context, args);
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
angular.module('app.layout')
.directive('classScroll', function ($window) {
return {
restrict: 'A',
link: function (scope, element) {
function toggle() {
angular.element(element)
.toggleClass('class-scroll--scrolled',
window.pageYOffset > 0);
scope.$applyAsync();
}
angular.element($window)
.on('scroll', debounce(toggle, 50));
toggle();
}
};
});
3. Если вам вообще не нужно запускать наблюдатели/дайджесты, используйте compile
.directive('classScroll', function ($window, utils) {
return {
restrict: 'A',
compile: function (element, attributes) {
function toggle() {
angular.element(element)
.toggleClass(attributes.classScroll,
window.pageYOffset > 0);
}
angular.element($window)
.on('scroll', utils.debounce(toggle, 50));
toggle();
}
};
});
И вы можете использовать его как <header class-scroll="header--scrolled">
Ответ 6
Директивы не являются "внутри мира angular", как говорится. Таким образом, вы должны использовать заявку, чтобы вернуться к ней при смене вещей.