Створення jQuery-плагінів

Плагін ScrollUp

Перетворіть ваш код з віджетом ScrollTop на jQuery-плагін.

Оскільки кнопка завжди вбудовується в body, немає потреби виконувати пошук елементів, до яких буде застосований даний плагін.

Тому виклик плагіна доцільно зробити звичайною функцією:

(function($){
  scrollUp();
})(jQuery);

Технічне завдання може виглядати приблизно так:

  1. При ініціалізації плагіна (створенні кнопки) перевірити - раптом кнопка вже є.
  2. Якщо в функцію передано аргумент - вважати його селектором для body (щоб можна було задати довільний клас, а не тільки .scrolltop).
  3. Добавити опцію швидкості прокрутки.

Події

Хороший плагін повинен мати підтримку подій для створення користувацьких (зовнішніх) реакцій.

Події у jQuery створюються за допомогою метода .trigger(назва_події):

...
function _scrollUp(){
  $(window).trigger('beforeScrollUp');
  $('html, body').animate({scrollTop: 0}, 500, function(){
    $(window).trigger('afterScrollUp');
  });
}

При написанні серйозних, абміційних плагінів варто замислитися про відсутність потенційних конфліктів. Зверніть увагу на назви подій у BootstrapJS.

Таким чином, назви ваших подій можуть мати такий вигляд (добавляємо в кінець назву плагіна):

$(window).trigger('beforeScrollUp.scrollUp');
$(window).trigger('afterScrollUp.scrollUp');

В деяких випадках корисно додавати спеціальні класи:

...
function _scrollTop(){
  $(window).trigger('beforeScrollUp');
  $('body').addClass('scrolling');
  $('html, body').animate({scrollTop: 0}, 500, function(){
    $(window).trigger('afterScrollUp');
    $('body').removeClass('scrolling');
  });
}

Даний код буде двічі викликати подію afterScrollUp тому що у нас в селекторі два елемента: html та body. Якщо це є проблемою, то код можна модифікувати наступним чином:

...
function _scrollTop(){
  $(window).trigger('beforeScrollUp');
  $('body').addClass('scrolling');
  $('html, body').animate({scrollTop: 0}, 500);
  setTimeout(function(){
    $(window).trigger('afterScrollUp');
    $('body').removeClass('scrolling');
  }, 500);
}

Як приклад - jQuery в момент анімації добавляє до елементів властивість :animated.

Завдання

  1. Добавити дві події: "початок скролла", "кінець скролла".
  2. Добавляйте до body клас scrollingUp на час роботи скролінга.
  3. Протестуйте події - створіть дві реакції: на початку скролла зробіть фон body жовтим, в кінці скролла - поверніть звичайний фон.

Команди

Команди - це керування плагіном іззовні (дистанційне керування).

Якщо плагін запустити не з опціями, а зі строковим аргументом, то вважається, що даний віджет повинен виконати вказану команду:

$.fn.myPlugin = function(s){
  if (s === 'назва_команди') {
    // код виконання команди
    ...
    return this; // або return false; якщо немає потреби повертати колекцію елементів
  }
  // код плагіна
  ...
};

Зверніть увагу, що команда "destroy" повинна не знищувати елемент, а повертати елемент до того стану, в якому він був до початку роботи плагіна.

Завдання

  1. Добавити підтримку команд 'scroll' та 'destroy'.

Плагін прокрутки до потрібної секції

Вгорі сторінки є меню навігації по секціям лендінга. Створіть плагін з наступними вимогами:

  1. Потрібно щоб справа на сторінці з'явилися напівпрозорі кружечки з індикацією поточної секції.
  2. Кружочки повинні ховатися на першому екрані лендінга і з'являтися коли користувач проскролить на половину висоти екрана.
  3. При наведенні курсора миші на кружечок - він повинен стати непрозорим, а зліва від нього повинен з'явитися напис - назва секції.
  4. При кліку на кружечок чи напис повинен відбутися плавний скролл до потрібної секції.
  5. Під час скролла сторінки повинен підсвічуватися відповідний кружечок, вздовж якої секції скроллимо.

Додаткове завдання: меню розташоване внизу блока header. При прокрутці до цього меню воно повинне прилипнути до верху сторінки.

Можна використати наступну заготовку:

section { min-height: 350px;}
menu li { display: inline-block;}
menu li a { display: block; padding: 12px 32px;}

<h1>Заголовок сторінки</h1>
<menu>
  <li><a href="#s1">Секція 1</a></li>
  <li><a href="#s2">Секція 2</a></li>
  ...
  <li><a href="#s7">Секція 7</a></li>
</menu>
<section id="s1">
  <h2>Секція 1</h2>
  <p>lorem</p>
</section>
<section id="s2">
  <h2>Секція 2</h2>
  ...

Плагін imageZoom

В багатьох інтернет-магазинах є можливість більш детально розглянути зображення товару, збільшуючи частину цього зображення в окремому блоці під час руху миші зображенню.

Створіть технічне завдання (план роботи над плагіном). Створіть сам плагін, користуючись подальшими відомостями.

Загальні відомості.

Плагіни розповсюджуються окремими js-файлами (за потреби - ще css, images, fonts...).

Якщо плагін потребує бібліотеку - назва цієї бібліотеки прописується перед назвою плагіна:

jquery.pbslider.min.js
bootstrap.finetimepicker.min.js
tagscloud.js

Не забувайте, що бібліотеку необхідно підключити перед плагіном.

Для назв файлів використовуйте тільки літери нижнього регістру, не використовуйте дифіси - буде менше проблем на linux-серверах.

Об'єкт jQuery містить внутрішню властивість - бібліотеку функцій:

console.dir(jQuery.fn);

Плагіни добавляють до цієї бібліотеки свою функцію (метод):

$.fn.mySuperPuperPlugin = function(){
  // код плагіна
  ...
};

Не забувайте, що jQuery завжди повертає набір елементів, тому необхідно перебрати цей набір:

$('.box').mySuperPuperPlugin();

$.fn.mySuperPuperPlugin = function(){
  // this - jQuery-набір всіх знайдених по селектору елементів
  this.each(function(){
    // код плагіна
    // this - кожен js-елемент
    ...
  });
};

Не забувайте про ланцюжкові виклики, в кінці функції поверніть this (якщо функція не повинна повертати щось інше):

$.fn.mySuperPuperPlugin = function(){
  // код плагіна
  ...
  return this;
};

Завдання

  1. В html-документ добавте пару зображень: <img src="..." alt="" class="zoom-image" width="500">
    <br><br>
    <img src="..." alt="" class="zoom-image" width="500">
  2. Зображення підбирайте великого розміру (2-8 Мп) і з дрібною деталізацією (плавні переходи кольорів нам не підійдуть).
  3. Створіть файли плагіна: jquery.zoomimage.js.
  4. Пропишіть jQuery-обгортку, добавте в бібліотеку jQuery нову функцію zoomImage, забезпечте ланцюжок викликів.

Функції та змінні плагіна

Плагін може мати сервісні (внутрішні) функції, змінні, їх можна прописати на початку плагіна (функції можна і в кінці плагіна).

Визначте необхідну область видимості функцій та змінних і вставте їх у відповідні місця кода.

(function($){
  var _days = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт'];
  function _randomItem(arr){ ... }

  $.fn.mySuperPuperPlugin = function(){
    var _speed = 700;
    function _getParent(e){ ... }

    // код плагіна
    ...
    return this;

    function _animateBox(elem){ ... }
  };

  function lunarTimeZone(){ ... }
  function marsTimeZone(){ ... }

})(jQuery);

Зауважте - символи підкреслення на початку імен змінних та функцій не обов'язкові, вони лише говорять, що дана функція є сервісною, приватною, не бажаною до використання сторонніми розробниками.

Завдання

  1. Відповідно до технічного завдання створіть функції, які б виконували окремі задачі плагіна.
  2. На даному етапі не заморачуйтесь з позицією бекграунда, пропишіть його як clientX та clientY.
  3. Зберіть функції до купи, щоб реалізувати плагін.

Параметри, опції плагіна

Параметри часто задаються у вигляді об'єкта:

var options = {
  speed: 300,
  duration: 4000,
  pause: true,
  effect: 'fade'
};
$('.box').plugName(options);

При цьому бажано в плагіні задати опції по-замовчуванню, в цьому нам допоможе jQuery-функція extend:

$.fn.myPlugin = function(options){
  var defaultOptions = {
    speed: 400,
    duration: 3000,
    pause: false,
    effect: 'slide',
    loop: 0
  };
options = $.extend(defaultOptions, options);
  // код плагіна
  ...
};

// коротший варіант:
$.fn.myPlugin = function(options){
  options = $.extend({
    speed: 400,
    duration: 3000,
    pause: false,
    effect: 'slide',
    loop: 0
  }, options);
  // код плагіна
  ...
};

Завдання

  1. Добавте в плагін опції розміру блока із зумом та лівий маргін.

Плагін fineSelect

Замовнику потрібен елемент select, який би підійшов до специфічного дизайну сайта: зі своєю іконкою стрілки, бордерами, стилізацією пунктів списку.

Напишіть технічне завдання, створіть план робот, базовий алгоритм та створіть плагін.

<p>
  <label>
    <select name="fruits" id="fruit" class="fine-select">
      <option>Яблука</option>
      <option>Грушки</option>
      <option>Сливки</option>
    </select>
  </label>
</p>

<p>
  <label>
    <select name="town" id="town" class="validate fine-select">
      <option disabled>оберіть місто:</option>
      <option selected value="km">Хмельницький</option>
      <option value="lv">Львів</option>
      <option value="te">Тернопіль</option>
      <option value="vn">Вінниця</option>
    </select>
  </label>
</p>

Плагін menuUI

Пропонується покращити функціональність випадаючого меню

В ОС Windows, Linux коли випадає меню третього рівня вліво чи вправо, то потрібно акуратно перемістити мишу горизонтально, а тоді вже - вертикально щоб вибрати потрібний пункт меню.

В MacOS та деяких програмах (phpStorm, наприклад) курсор миші можна провести по діагоналі одразу до потрібного пункту.

Потрібно створити плагін, що дозволяє переміщати курсор миші по діагоналі до потрібного пункту меню.

Подумайте як це зробити, опишіть алгоритм та створіть плагін.

Плагін Carousel

Заготовка.

Потрібно створити плагін слайдера (анімованої галереї, каруселі).

  1. Базовий HTML-код - мінімалістичний, використовуйте div або ul.
  2. Підтримка опцій, подій та команд.
  3. Елементи керування повинні створюватися динамічно або підключатися існуючі.
  4. Повинна бути підтримка декількох варіантів анімації: fade, slide, zoom, rotate, break4x, random.

Для першого слайдера підберіть невеликі зображення однакового розміру, наприклад, 640х480. Якщо зображення будуть різного розміру - контент під слайдером буде скакати при кожному перемиканні слайдів.

Можете використати наступний код:

.sl-wrap { position: relative; display: inline-block; padding:20px 60px 60px;}
.sl-wrap .pager { padding: 16px 0 0; text-align: center; }
.sl-wrap .pager li { display: inline-block; margin: 0 4px; width: 21px; height: 21px; line-height: 21px; border: 1px solid black; border-radius: 50%; opacity: .6; cursor: pointer;}
.sl-wrap .pager li:hover { opacity: 1;}
.sl-wrap .pager li.active { color: white; background: red; cursor: default;}
.sl-wrap .navi span { position: absolute; left: 0; top: 50%; margin: -90px 0 0; height: 60px; font-size: 60px; line-height: 60px; font-weight: bold; opacity: .3; cursor: pointer;}
.sl-wrap .navi span.next { left: auto; right: 0;}
.sl-wrap .navi span:hover { opacity: 1;}
.sl-wrap .slider1 img { display: block;}
.sl-wrap .slider2 { margin: 0; padding: 0;}
.sl-wrap .slider2 li { display: block; padding: 0 40px; width: 240px; text-align: center; border: 1px solid silver;}
.sl-wrap .slider2 strong { display: block; padding: 40px 0 20px; font-size: 40px; line-height: 54px; font-style: italic;}
.sl-wrap .slider2 span { display: block; padding: 0 0 40px; font-size: 24px; line-height: 30px;}
.sl-wrap .pager,
.sl-wrap .navi { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;}

<div class="sl-wrap">
  <div class="slider slider1">
    <img src="img1.jpg" alt="image">
    <img src="img2.jpg" alt="image">
    <img src="img3.jpg" alt="image">
  </div>
</div>

<hr>

<div class="sl-wrap">
  <ul class="slider slider2">
    <li>
      <strong>Slide 1</strong>
      <span>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</span>
    </li>
    <li>
      <strong>Slide 2</strong>
      <span>Amet assumenda blanditiis corporis eius error!</span>
    </li>
    <li>
      <strong>Slide 3</strong>
      <span>Ipsum dolor sit amet, consectetur adipisicing elit.</span>
    </li>
    <li>
      <strong>Slide 4</strong>
      <span>Dolor sit amet, consectetur adipisicing elit.</span>
    </li>
  </ul>
</div>

Елементи керування слайдером:

<p class="navi"><span class="prev"><</span> <span class="next">></span></p>

<ul class="pager">
  <li class="active">1</li>
  <li>2</li>
  <li>3</li>
...
</ul>

Рефакторінг кода на прикладі функцій слайдера

Рефакторинг (англ. refactoring) — перетворення програмного коду, зміна внутрішньої структури програмного забезпечення для полегшення розуміння коду і легшого внесення подальших змін без зміни зовнішньої поведінки самої системи.

Курс по рефакторингу.

Наприклад, є у нас три функції (записано псевдокодом):

function nextSlide:
  clearTimeout;
  new_slide = slides.next or slides.first;
  active_slide.removeClass('active').hide;
  new_slide.addClass('active').show;
  setTimeout(nextSlide);

function prevSlide:
  clearTimeout;
  new_slide = slides.prev or slides.last;
  active_slide.removeClass('active').hide;
  new_slide.addClass('active').show;
  setTimeout(nextSlide);

function toSlide(n):
  clearTimeout;
  new_slide = slides.eq(n);
  active_slide.removeClass('active').hide;
  new_slide.addClass('active').show;
  setTimeout(nextSlide);

Бачимо, що значна частина коду повторюється:

function nextSlide:
  clearTimeout;
  new_slide = slides.next or slides.first;
  active_slide.removeClass('active').hide;
  new_slide.addClass('active').show;
  setTimeout(nextSlide);

function prevSlide:
  clearTimeout;
  new_slide = slides.prev or slides.last;
  active_slide.removeClass('active').hide;
  new_slide.addClass('active').show;
  setTimeout(nextSlide);

function toSlide(n):
  clearTimeout;
  new_slide = slides.eq(n);
  active_slide.removeClass('active').hide;
  new_slide.addClass('active').show;
  setTimeout(nextSlide);

В нашому випадку міняється тільки спосіб визначення наступного слайда в слайдері.

Таким чином, можна всі три функції об’єднати в одну, а те, що відрізняється, виконувати по якимось умовам.

Один з варіантів - добавити ще один параметр у функцію (для функції toSlide цей параметр вже задано).

Придумуємо умову:
1). якщо n == 'next', то треба переключити на наступний слайд;
2). якщо n == 'prev', то треба переключити на попередній слайд;
3). якщо n == 0, 1, 2, 3 і т.д., то переключити на слайд №n.

function newSlide(n):
  clearTimeout;
  if n == 'prev':
    new_slide = slides.prev or slides.last;
  else if n == 'next':
    new_slide = slides.next or slides.first;
  else:
    new_slide = slides.eq(n);
  active_slide.removeClass('active').hide;
  new_slide.addClass('active').show;
  setTimeout(nextSlide);

Умову ще можна скоротити за рахунок використання аргумента як імені функції та використання тернарного оператора:

if typeof n === 'string':
  new_slide = slides[n];
else:
  new_slide = slides.eq(n);
if !new_slide.length:
  new_slide = slides[(n === 'prev' ? 'last' : 'first')];