22 декабря 2015
21 января 2011
18
Айфонные UI-прелести с помощью jQuery: UITableView
Всем привет.
Несколько лет назад, как только мне в руки попал айпод, и я увидел, как там организованы списки песен, мне сразу захотелось реализовать такое же с помощью JavaScript.
Но до вчерашнего дня все не находилось времени.
Суть скрипта в том, что он позволяет заголовку списка «зависать» над содержимым, пока мы его перематываем и не дойдем до следующего заголовка.
Чтобы сразу все стало ясно, приведу скриншот списка песен:
Пока мы перематываем песни, начинающиеся на «А», заголовок продолжает висеть над списком. А как только приближается следующий заголовок «B», он плавно смещает предыдущий и становится на его место.
В библиотеке UIKit для разработки iOS-приложений такой элемент интерфейса называется UITableView.
Кстати, такой элемент в интерфейсе, назовем его «подвисающий заголовок», еще есть в Пикасе. Если на айфоне этот подвисающий заголовок не очень-то функционален, то в Пикасе эта функция очень даже оправдана. Во-первых всегда позволяет понять, какую коллекцию сейчас просматриваешь, во-вторых выполнить какие-то функции.
В вебе я примеров пока не встречал, хотя область полезного применения безгранична.
Итак, к делу.
Начнем с разметки. У нас будет список, состоящий из заголовков и их содержимого. Содержимое — это ненумерованный список.
<body>
<div id="TableViewWrapper">
<dl id="TableView">
<dt class="header">First Header</dt>
<dd>
<ul>
<li class="item">Item 1</li>
<li class="item">Item 1</li>
<li class="item">Item 1</li>
<li class="item">Item 1</li>
<li class="item">Item 1</li>
<li class="item">Item 1</li>
</ul>
</dd>
<dt class="header">Second Header</dt>
<dd>
<ul>
<li class="item">Item 1</li>
<li class="item">Item 3</li>
<li class="item">Item 1</li>
<li class="item">Item 1</li>
</ul>
</dd>
<dt class="header">Third Header</dt>
<dd>
<ul>
<li class="item">Item 3</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
<li class="item">Item 2</li>
<li class="item">Item 1</li>
<li class="item">Item 1</li>
<li class="item">Item 3</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
</ul>
</dd>
</dl>
</div>
</body>
Внешний div#TableViewWrapper нам необходим и я позже поясню почему.
CSS:
body
{
font-family: Trebuchet MS;
background: #F7F4EA;
}
body, ul,li, dl, dd,dt {
margin: 0;
padding: 0;
}
li
{
list-style: none;
}
.header
{
line-height:40px;
font-weight: bold;
background: transparent url(bg.png) left top repeat;
padding: 0 10px;
color: #fff;
}
.item
{
border-top: solid 1px #999;
line-height: 80px;
padding: 0 10px;
background: #fefefe;
}
.item:first-child
{
border-top: none;
}
#TableViewWrapper
{
width: 400px;
height: 500px;
margin: 10px auto;
background:#fafafa;
position:relative;/*это важно*/
overflow:hidden;
}
#TableView
{
width: 100%;
height: 100%;
overflow:auto;
}
Ловим событие скролла на элементе #TableView:
$('#TableView').scroll(function () {
//здесь ловятся события прокрутки колесика мышки, перетаскивания ползунка или перемотки с помощью тач-пада
});
При каждом событии скролла, мы будем пробегаться по всем заголовкам и смотреть их позицию. Для того чтобы узнать позицию, используется функция .position(). Ее следует отличать от функции .offset(), потому что .position() возвращает позицию элемента относительно ближайшего оффсет-парента. Прошу прощения, не могу перевести нормально. Другими словами, относительно элемента, у которого position указан как relative, absolute или fixed.
Именно поэтому нам пришлось обернуть #TableView в #TableViewWrapper и прописать у него position: relative. Для того, чтобы корректно определять позицию заголовка относительно этого элемента.
Итак, если заголовок уперся в верхнюю часть видимой части экрана, то его позиционирование меняем на абсолютное и прикрепляем к верхней крышке. Ну и открепляем, когда содержимое спускается ниже заголовка. Словами непросто описать. Если хочется разобраться, рекомендую вникнуть в код и поиграть с фаербагом.
Функция, которая будет выполнять всю тяжелую работу, выглядит достаточно просто:
function HandleHeaderPosition(TableView) {
$('.header', TableView).each(function (i) {
var headerWidth = $(this).width();
var headerHeight = $(this).height();
var currentHead = $(this);
var currentContent = $(this).next();
var dif = currentContent.position().top + currentContent.height();
// если заголовок поднялся выше крыши, открепить его
if (currentHead.position().top <= 0) {
var top = dif > 0 ? 0 : dif;
currentHead.css({ position: 'absolute', top: top, width: headerWidth });
currentContent.css({ marginTop: currentHead.height() });
}
// если заголовок сдвинулся ниже высоты, от вкрепить назад в список
if (currentContent.position().top >= headerHeight
//или контент спустился ниже заголовка, а заголовок откреплен
|| (currentHead.position().top <= 0 && currentContent.position().top > 0)) {
currentHead.css({ position: 'inherit', width: 'auto' });
currentContent.css({ marginTop: 0 });
}
});
}
Вот, собственно, и все.
Для того, чтобы перемотка была плавной во всех браузерах (в опере перемотка плавная по-умолчанию), в демо я добавил плагин jquery.mousewheel (об этом плагине я упоминал в другой статье об айфонных ui-прелестях). От него можно избавиться — это не повлияет на функциональность.
http://www.google.com.ua/images?hl=ru&q=flowers
http://nodejs.org/docs/v0.2.6/api.html
Проще было бы запомнить все позиции заголовков, чем каждый раз высчитывать по новой. Тогда бы быстрее работало.
Предложенный мной вариант списка не похож на то, что вы показали.
senik11,
Сейчас мое тело находится в Минске, да.
Вано,
Буду применять. В будущих статьях будет раскрыто, где.
sergy, Сергей
Тут есть много что можно улучшить и исправить. Очень плохо работает с тач-падом, особенно на маке. Будем называть то, что есть прототипом.
Чем он не похож? о_О
Если бы на айфоне или в пикасе были такие заголовки как на Node.js, то это было бы не привлекательной фишкой интерфейса, а его уродством.
А я хотел именно эту привлекательность приподнести: плавное толкание одного заголовка другим, а не обыкновенную смену.
"Суть скрипта в том, что он позволяет заголовку списка «зависать» над содержимым, пока мы его перематываем и не дойдем до следующего заголовка."
Ваши ведь слова? =) А все что идет дальше это уже нюансы конкретного частного примера.
А я всего лишь ответил на вашу фразу:
"В вебе я примеров пока не встречал"
имея ввиду идею, а не способ реализации.
Вот пример реального применения такого эффекта:
http://docs.pravo.ru/document/view/59178
Очень удобная вещь.
Впрочем, всё равно извините за смуту. Прикольно придумали ;-) Спасибо.
У меня вопрос, как такую прокрутку сделать на чистом Javascript?
Основное внимание при этом обращая на вес кода в килобайтах.
Я можно сказать ещё новичок, но синтаксис javascript понимаю хорошо,
буду благодарен (кликая рекламу!)) за краткое руководство по написанию данного скрипта
(что в него должно входить, с чего начать и т.д.)