Javascript-форум (https://javascript.ru/forum/)
-   jQuery (https://javascript.ru/forum/jquery/)
-   -   Jquery viewport - проблема реализации адаптивного меню (https://javascript.ru/forum/jquery/43115-jquery-viewport-problema-realizacii-adaptivnogo-menyu.html)

Иззет 22.11.2013 19:04

Jquery viewport - проблема реализации адаптивного меню
 
Здравствуйте, готовлю сайт-одностраничник - 5 блоков на 100% ширины и высоты и фиксированное меню из якорных ссылок, ведущих к этим блокам. Нужно, чтобы при прокрутке вручную или с помощью меню, при нахождении в каком-либо из 5-и блоков соответствующий пункт меню получал класс "selected". В ходе поисков нашёл плагин jquery viewport и прилагающийся скрипт, но что-то не смог адаптировать под свой сайт:

// add & remove active link
$(document).ready(function() {
$(window).scroll(function () {
var inview = '#' + $("section > article:in-viewport:first").parent().attr('id'),
$link = $('nav a').filter('[hash=' + inview + ']')

if ($link.length && !$link.is('.selected')) {
$('nav a').removeClass('selected');
$link.addClass('selected');
}
})
});

mi.rafaylik 23.11.2013 00:41

ловить .scroll и проверять совпадение с позицией блоков: http://learn.javascript.ru/play/R6einb

Иззет 23.11.2013 00:58

По сути нужно, чтобы javascript сравнивал id блока, в кот-ом мы сейчас находимся, с адресом якорной ссылки (в адресе - id блока). И при их совпадении он должен давать класс соответствующей ссылке.

mi.rafaylik 23.11.2013 01:53

Цитата:

Сообщение от Иззет
сравнивал id блока, в кот-ом мы сейчас находимся с адресом якорной ссылки

Чтоб понять где мы находимся, сначала нужно сравнивать $(document).scrollTop() и $(elements).offset().top
А тогда уже искать в меню соответствующий пункт.

Посмотреть
<div id="nav">
	<a href="one" class="actual">one</a>
	<a href="two">two</a>
	<a href="three">three</a>
</div>

<div class="scroll selected" id="one">one</div>
<div class="scroll" id="two">two</div>
<div class="scroll" id="three">three</div>

$('#nav a').click(function() {
	var id = $(this).attr('href'); // идентификатор блока с нужным якорем
	var y = $('#'+id).offset().top; // координаты начала блока
	$('body').animate({scrollTop:y}, 'slow'); // катимся к якорю + инициируем скроллинг
	return false; // отмена стандартного действия (перехода по ссылке)
});

var lastID; // для id последнего посещённого блока, которому сменили класс
var elemID; // для id текущего блока, которому будем добавлять класс

$(window).scroll(function() {
	var yDoc = $(document).scrollTop(); // координаты текущего скроллинга по вертикали
	$('.scroll').each(function() { // прогуляемся по блокам (надеюсь их немного)
		var yElem = $(this).offset().top; // координаты позиции каждого блока по вертикали
		if (yDoc >= yElem && yDoc < (yElem+50)) { // если верх блока достиг верха страницы + запас 50px
			elemID = $(this).attr('id'); // id блока которому будем добавлять класс
			if (elemID != lastID) { // меняем класс только один раз для текущего блока (а не каждый пиксель скроллинга)
				lastID = elemID; // запоминаем, что здесь класс мы уже добавили
				mark($('#nav a[href='+elemID+']'), $(this)); // смена классов для блока и пункта меню
			}
		}
	});
});

function mark(nav, elem) { // функция смены классов
	$('#nav a').removeClass('actual');
	nav.addClass('actual');
	$('.scroll').removeClass('selected');
	elem.addClass('selected');
}

Иззет 23.11.2013 13:37

Большое спасибо за помощь, но я что-то затрудняюсь подключить скрипт. Мне на самом деле нужно чтобы только ссылка класс получала, вписал код так:
var lastID; // здесь будем хранить id последнего посещённого блока, которому сменили класс
$(window).scroll(function() {
var yDoc = $(document).scrollTop(); // координаты текущего скроллинга по вертикали
$('.scroll').each(function() { // прогуляемся по блокам (надеюсь их немного)
var yElem = $(this).offset().top; // координаты позиции блока по вертикали
if (yDoc >= yElem && yDoc < (yElem+50)) { // диапазон между верхом блока + запас (если скроллинг быстрый)
var id = $(this).attr('id');
if (id != lastID) { // для каждого блока меняем класс только один раз, нечего повторяться повторяться
lastID = id; // запоминаем, что здесь класс мы уже добавили
mark($('li a[href='+id+']'); // смена классов для блока и пункта меню
}
}
});
});
function mark(nav) { // функция смены классов
$('li a').removeClass('selected');
nav.addClass('selected');
}
может где-то ошибся или какую-то конкретную библиотеку jquery подключить надо ? (все соответствующие классы прописал)

mi.rafaylik 23.11.2013 15:54

Версия фреймворка в данном случае не важна, скорее всего Ваш html отличается от моего примера.
Обратите внимание на .css в примере. Класс, которым подсвечиваем ссылки в меню, имеет !important

Сначала нужно понять, как работает данная реализация:
1. ловим событие $(window).scroll()
2. на каждой итерацию (каждый пиксель скроллинга) получаем текущую позицию прокрутки документа $(document).scrollTop()
3. на каждой итерации также проверяем, не совпадает ли позиция прокрутки документа с позицией каждого из элементов, но...
4. для этого (если элементов много) проходим по элементам циклом и работаем с каждым
5. для каждого элемента смотрим его положение в документе $(element).offset().top
6. сравниваем наши пункты 2. и 5.: if ($(document).scrollTop() >= $(element).offset().top) {...}
7. но при быстром скроллинге скрипт может не успеть отловить совпадение, поэтому расширяем пункт 6. и добавляем в if (...) вторую границу диапазона (50px от верхней границы элемента): && $(document).scrollTop() < ($(element).offset().top + 50)
8. если условие выполнилось, смотрим id элемента, с которым работаем в данный момент в цикле: $(element).attr('id')
9. используем переменные (lastID, elemID) чтобы лишний раз не добавлять класс ссылке в меню
10. находим в меню ссылку, атрибут которой соответствует id нашего элемента: $('menu a[href='+$(element).attr('id')+']')
11. присваиваем ссылке в меню класс, предварительно убрав класс у остальных ссылок.

Упростил пример: Посмотреть
.

Иззет 23.11.2013 17:21

Большое спасибо, заработало) только одна проблема - класс ссылкам передаётся только при обратном скроллинге, т.е. снизу вверх, с чем это связано?

mi.rafaylik 23.11.2013 18:36

Иззет,
Сложно сказать, не увидев Вашего кода.
Покажите всю страницу (html, css, js) в песочнице: http://learn.javascript.ru/play

Иззет 23.11.2013 18:59

В вашем последнем примере, "Упростил пример: Посмотреть" тоже есть эта проблема, попробуйте вручную прокрутить вниз и, прокручивая сверху вниз, можно увидеть, что ссылке "two" класс "actual" не даётся.

mi.rafaylik 23.11.2013 19:24

Проверил в Safari, Chrome, Firefox, Opera - работает, каждая ссылка получает свой класс.
К сожалению в IE проверить не могу, т.к. Mac OS X.
Хм... чтоб понять в чём дело, попробуйте:
1. прокручивать медленнее или быстрее.
2. увеличить интервал захвата, не 50 а например 100, возможно, скрипт не успевает совершить проверку при быстром скроллинге.


Часовой пояс GMT +3, время: 15:03.