Введение в jQuery

Дополнение

Разбить на страницы
Показывать лекцию целиком

Дополнение

Еще чуть-чуть о полезном инструментарии: есть такой классный плагин – jQuery-inlog [http://prinzhorn.github.com/jquery-inlog/] – основное его назначение — дать нам чуть-чуть больше понимания о происходящем внутри самого jQuery, вот кусочек HTML:

<body>
<div class="bar">
<div class="bar">
<div id="foo"></div> 
</div>
</div>
<div id="bacon"></div> </body>

А вот и код, который его обслуживает:

$l(true);
$("#foo").parents(".bar").next().prev().parent().fadeOut();
$l(false);

Какие-то странные манипуляции, для какого же элемента будет применён метод "fadeout()"? Для выяснения оного наш код обёрнут в вызов метода "$l()". "$l()" — это и есть собственно вызов плагина, результат его работы можно найти в консоли:

У данного плагина есть ещё настройки, которые регулируют объём информации выводимой в консоль.

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

jQuery UI

jQuery UI представляет из себя набор виджетов и плагинов от самих разработчиков jQuery. По моему мнению, данный инструмент необходимо изучить настолько, насколько это требуется чтобы не писать свои "велосипеды". Скачать-почитать о данной надстройке над jQuery можно на домашней страницы проекта – http://jqueryui.com/

Что нам необходимо знать о виджетах и плагинах? Первое – это какие они есть, и второе – как работают. На этих двух моментах я и постараюсь остановиться.

Интерактивность

Начну с полезных плагинов, которые могут упростить жизнь при создании интерактивных интерфейсов:

  • Draggable [http://jqueryui.com/position/] – данный компонент позволяет сделать любой DOM элемент перетаскиваемым при помощи мыши
  • Droppable [http://jqueryui.com/droppable/] – это логичное продолжение draggable компонента, необходим для работы с контейнерами, внутрь которых можно перетаскивать элементы
  • Resizable [http://jqueryui.com/resizable/] – как следует из название – даёт возможность растягивать любые DOM элементы
  • Selectable [http://jqueryui.com/selectable/] – позволяет организовать "выбор" элементов, удобно использовать для организации менеджмента картинок
  • Sortable [http://jqueryui.com/sortable/] – сортировка DOM элементов
  • Виджеты

    Виджеты – это уже комплексное решение содержащие не только JavaScript код, но и некую HTML и CSS реализацию:

  • Accordion – данный виджет следует использовать если у вас уже используется jQuery UI в проекте, сам по себе основной его функционал можно реализовать в несколько строк:
    <!DOCTYPE html>
    <html dir="ltr" lang="en-US">
    <head>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Accordion</title>
        <link rel="profile" href="http://gmpg.org/xfn/11"/>
        <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/>
        <link rel="stylesheet" href="css/styles.css"/>
        <script type="text/javascript" src="js/jquery.js"></script>
        <script type="text/javascript" src="js/code.js"></script>
        <script type="text/javascript">
            $(function(){
                var toDisplay = 1;
                $("article h2").click(function(){
                    var $this = $(this);
                    $this
                        .next("p")
                            .slideToggle("slow")
                        .siblings("p:visible")
                            .slideUp("slow");
                    $this.toggleClass("active");
                    $this.siblings("h2").removeClass("active");
                })
                    .eq(toDisplay).addClass("active")
                    .next().show();
            });
        </script>
        <style type="text/css">
            article h2 {
                background: #e9e7e7 url(images/arrows.png) no-repeat 618px 7px;
                padding: 7px 15px;
                margin: 0;
                border: solid 1px #c4c4c4;
                border-bottom: none;
                cursor: pointer;
            }
            article h2:hover {
                background-color: #e3e2e2;
            }
            article h2.active {
                background-position: 618px -23px;
            }
            article p {
                display: none;
                margin: 0;
                padding:  10px 15px 20px;
            }
        </style>
    </head>
    <body>
        <div id="content" class="wrapper box">
            <menu>
                <a href="index.html" title="go prev" class="button alignleft" rel="prev">← Back </a>
                <a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a>
                <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a>
                <a href="tabs.html" title="go next" class="button alignright" rel="next">Next →</a>
            </menu>
            <header>
               <h1>Реализация аккордиона</h1>
               <h2>На jQuery без использования jQuery UI</h2>
            </header>
            <article>
                <h2>Article Title</h2>
                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rutrum,
                lectus eu varius consectetur, libero velit hendrerit augue, ut posuere enim neque
                in libero. Donec eget sagittis nibh. Suspendisse sed tincidunt urna. Cras quis
                euismod neque. Maecenas auctor ultricies posuere. Pellentesque luctus pulvinar dui
                eget semper. Donec sodales odio eu sapien varius luctus. Donec dictum feugiat diam
                at malesuada. Sed nec massa in augue condimentum faucibus quis ut diam. Quisque
                nisl sem, semper nec vulputate vel, mattis sit amet justo. Aliquam purus felis,
                tempor at scelerisque quis, tincidunt in neque. Etiam ut risus diam. Pellentesque
                fermentum risus id elit feugiat cursus. Ut fringilla dictum diam, sed iaculis
                lorem pulvinar ut. Cras vel elit id velit commodo viverra sit amet vel orci.</p>
                <h2>Article Title</h2>
                <p>Duis in vestibulum sem. Cras euismod tincidunt dui, et scelerisque tellus condimentum vel.
                Maecenas et urna sit amet risus fermentum rhoncus nec porttitor ligula. Maecenas sit amet
                turpis enim, ut iaculis est. Duis feugiat, lacus id placerat porttitor, lorem augue gravida
                nisi, eu porta eros risus et lectus. Maecenas vestibulum nunc vel ipsum tincidunt sit amet
                blandit sapien bibendum. Proin vel vulputate nisl. Duis tempor imperdiet placerat. Pellentesque
                faucibus consequat magna, et bibendum nisl egestas non. Pellentesque sit amet mattis augue.
                Aenean at diam tincidunt purus sollicitudin gravida non in nisi. Fusce bibendum, magna in
                adipiscing mattis, sem risus fringilla mi, nec gravida lectus lectus at nibh. Suspendisse
                adipiscing elementum laoreet. Suspendisse sem erat, varius quis aliquet vitae, dapibus sed
                nibh. Nullam iaculis sem at mauris faucibus in vestibulum libero pretium. Aliquam eu turpis
                libero. Fusce et ultrices lectus.</p>
                <h2>Article Title</h2>
                <p>Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
                Morbi dapibus, dui in consequat pulvinar, nunc erat consequat ipsum, ut blandit nulla eros ac purus.
                In at justo sed lorem consequat fermentum. Nunc scelerisque purus sed neque rutrum adipiscing. Sed
                vehicula tristique dolor, quis porttitor magna dictum eu. Cras tristique erat non orci aliquam tempor.
                In hac habitasse platea dictumst. Nam eget sodales neque. Aenean adipiscing consequat diam, eu
                molestie diam dictum eu.</p>
            </article>
            <footer>
               ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a>
            </footer>
            <script type="text/javascript">
                var _gaq = _gaq || [];
                _gaq.push(['_setAccount', 'UA-1669896-2']);
                _gaq.push(['_trackPageview']);
                (function() {
                 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
                 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
                })();
            </script>
        </div>
    </body>
    </html>
  • Autocomplete – как и следует из название, данный виджет отвечает за добавление функции автодополнения к полям ввода, естественно с поддержкой AJAX
  • Button – создание кнопок используя JavaScript – ещё тот моветон, но возможно пригодится, если вы сильно завязались на jQuery UI:
  • Datepicker – если ваш браузер не поддерживает в полной мере спецификацию HTML5 и <input type="date"/> в частности, то потребуется эмуляция данной возможности с помощью виджета datepicker:
  • Dialog – виджет предназначенный для создания слегка неуклюжих диалоговых окон:
  • Menu – создание меню из списка, с поддержкой вложенности
  • Progressbar – название говорит само за себя, и да в HTML5 он тоже включён:
  • Slider – ещё один виджет для устаревших браузеров:
  • Spinner – ещё один удобный контрол для форм, опять же – в HTML5 уже есть:
  • Tabs – они же табы – достаточно популярный элемент в web-разработке, и так же как и "accordion" вполне заменяем простым кодом
    <!DOCTYPE html>
    <html dir="ltr" lang="en-US">
    <head>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Tabs</title>
        <link rel="profile" href="http://gmpg.org/xfn/11"/>
        <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/>
        <link rel="stylesheet" href="css/styles.css"/>
        <script type="text/javascript" src="js/jquery.js"></script>
        <script type="text/javascript" src="js/code.js"></script>
        <script type="text/javascript">
            $(function(){
                var hash = window.location.hash;
                $("article li a").click(function(){
                    var $this = $(this);
                    var $pan = $("article").find($this.attr('href'));
                    if (!$this.hasClass('active')) {
                        $("article li a").removeClass("active");
                        $("article p").hide();
                        $this.addClass("active");
                        $pan.show();
                        window.location.hash = $this.attr('href');
                    }
                    return false;
                });
                if (hash  $("article li a[href="+hash+"]").length) {
                    $("article li a[href="+hash+"]").click();
                } else {
                    $("article li a").eq(0).click();
                }
            });
        </script>
        <style type="text/css">
            article ul {
                padding: 0 15px 0;
                margin-bottom: -1px;
                overflow: hidden;
            }
            article li {
                float:left;
                list-style: none;
                margin: 0 4px;
            }
            article li a {
                display: block;
                padding: 4px 8px;
                background: #f5f5f5;
                font-size: 14px;
                border: 1px solid #ccc;
                border-radius: 2px 2px 0 0;
            }
            article p {
                clear: both;
                display: none;
                margin: 0 4px;
                padding:  10px 15px 20px;
                border: 1px solid #ccc;
                border-radius: 2px;
            }
            article a.active {
                background: #fff;
                border-bottom: 1px solid #fff;
            }
        </style>
    </head>
    
    <body>
    <div id="content" class="wrapper box">
           <menu>
               <a href="accordion.html" title="go prev" class="button alignleft" rel="prev">← Prev </a>
               <a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a>
               <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a>
               <a href="widget.html" title="go next" class="button alignright" rel="next">Next →</a>
           </menu>
           <header>
               <h1>Реализация закладок</h1>
               <h2>На jQuery без использования jQuery UI</h2>
           </header>
           <article>
               <ul>
                   <li><a href="#pan1">First Article</a></li>
                   <li><a href="#pan2">Second Article</a></li>
                   <li><a href="#pan3">Third Article</a></li>
               </ul>
    
               <p id="pan1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rutrum,
               lectus eu varius consectetur, libero velit hendrerit augue, ut posuere enim neque
               in libero. Donec eget sagittis nibh. Suspendisse sed tincidunt urna. Cras quis
               euismod neque. Maecenas auctor ultricies posuere. Pellentesque luctus pulvinar dui
               eget semper. Donec sodales odio eu sapien varius luctus. Donec dictum feugiat diam
               at malesuada. Sed nec massa in augue condimentum faucibus quis ut diam. Quisque
               nisl sem, semper nec vulputate vel, mattis sit amet justo. Aliquam purus felis,
               tempor at scelerisque quis, tincidunt in neque. Etiam ut risus diam. Pellentesque
               fermentum risus id elit feugiat cursus. Ut fringilla dictum diam, sed iaculis
               lorem pulvinar ut. Cras vel elit id velit commodo viverra sit amet vel orci.</p>
               <p id="pan2">Duis in vestibulum sem. Cras euismod tincidunt dui, et scelerisque tellus condimentum vel.
               Maecenas et urna sit amet risus fermentum rhoncus nec porttitor ligula. Maecenas sit amet
               turpis enim, ut iaculis est. Duis feugiat, lacus id placerat porttitor, lorem augue gravida
               nisi, eu porta eros risus et lectus. Maecenas vestibulum nunc vel ipsum tincidunt sit amet
               blandit sapien bibendum. Proin vel vulputate nisl. Duis tempor imperdiet placerat. Pellentesque
               faucibus consequat magna, et bibendum nisl egestas non. Pellentesque sit amet mattis augue.
               Aenean at diam tincidunt purus sollicitudin gravida non in nisi. Fusce bibendum, magna in
               adipiscing mattis, sem risus fringilla mi, nec gravida lectus lectus at nibh. Suspendisse
               adipiscing elementum laoreet. Suspendisse sem erat, varius quis aliquet vitae, dapibus sed
               nibh. Nullam iaculis sem at mauris faucibus in vestibulum libero pretium. Aliquam eu turpis
               libero. Fusce et ultrices lectus.</p>
               <p id="pan3">Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
               Morbi dapibus, dui in consequat pulvinar, nunc erat consequat ipsum, ut blandit nulla eros ac purus.
               In at justo sed lorem consequat fermentum. Nunc scelerisque purus sed neque rutrum adipiscing. Sed
               vehicula tristique dolor, quis porttitor magna dictum eu. Cras tristique erat non orci aliquam tempor.
               In hac habitasse platea dictumst. Nam eget sodales neque. Aenean adipiscing consequat diam, eu
               molestie diam dictum eu.</p>
           </article>
           <footer>
               ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a>
           </footer>
            <script type="text/javascript">
                var _gaq = _gaq || [];
                _gaq.push(['_setAccount', 'UA-1669896-2']);
                _gaq.push(['_trackPageview']);
                (function() {
                 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
                 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
                })();
            </script>
    </div>
    </body>
    </html>
  • Tooltip – вот и последний виджет – всплывающие подсказки, простой и должен быть востребован, ну а там жизнь покажет
  • На этом обзор виджетов можно считать законченным, вернёмся к плагинам.

    Все виджеты и плагины завязаны на ядро jQuery UI, но есть так же зависимости между самими плагинами и стоит о них помнить. Но не переживайте – при сборке jQuery UI пакета все зависимости проверяются автоматически, т.е. когда вам потребуется доселе неподключенный виджет, лучше скачать сборку заново.

    Утилиты

    Утилит у нас не много – вот полезный плагин – position, который позволяет контролировать положение DOM элементов – http://jqueryui.com/position/, а ещё есть фабрика по созданию виджетов, но о ней я расскажу чуть попозже.

    Эффекты

    Среди эффектов предоставляемых jQuery UI я выделяю четыре пункта:

  • Анимация цвета
  • Анимация изменения классов
  • Набор эффектов
  • Расширения возможностей easing
  • За анимацию цвета отвечает компонент "Effects Core", который позволяет анимировать изменения цвета посредством использования функции "animate()":

    $("#my").animate({ backgroundColor: "black" }, 1000);

    Да-да, jQuery из коробки не умеет этого делать, а вот jQuery UI позволяет анимировать следующие параметры:

  • backgroundColor
  • borderBottomColor
  • borderLeftColor
  • borderRightColor
  • borderTopColor
  • color
  • outlineColor
  • Ещё одной возможностью заключенной в "Effects Core" является анимация изменений класса DOM элемента, т.е. когда вы будете присваивать новый класс элементу, то вместо обычного моментального применения новых CSS свойств вы будете наблюдать анимацию этих свойств от текущих до заданных в присваиваемом классе. Для использования данного функционала нам потребуются старые знакомые – методы "addClass()", "toggleClass()" и "removeClass()", с одной лишь разницей – при вызове метода вторым параметром должна быть указана скорость анимации:

    $("#my").addClass("active", 1000);
    $("#my").toggleClass("active", 1000);
    $("#my").removeClass("active", 1000);

    Если из предыдущего абзаца у вас не возникло понимания сути происходящего, то этот код для вас:

    <style>
    #my {
    font-size:14px;
    }
    #my.active {
    font-size:20px;
    }
    </style>
    <script>
    $(function (){
    $("#my").addClass("active", 1000);
    // тут получается аналогично следующему вызову
    $("#my").animate({"font-size":"20px"}, 1000);
    });
    </script>

    А ещё появляется метод switchClass(removeClass, addClass, duration), который заменяет один класс другим, но мне он ни разу не пригодился.

    О наборе эффектов я не буду долго рассказывать, их лучше посмотреть в действии на странице http://jqueryui.com/effect/. Для работы с эффектами появляется метод "effect()", но сам по себе его лучше не использовать, ведь UI расширил функционал встроенных методов "show()", "hide()" и "toggle()", теперь, передав в качестве параметра скорости анимации названия эффекта вы получите необходимый результат:

    $("#my").hide("puff");
    $("#my").show("transfer");
    $("#my").toggle("explode");

    Приведу список эффектов, может кто запомнит: blind, bounce, clip, drop, explode, fold, highlight, puff, pulsate, scale, shake, size, slide, transfer.

    Помните, я в главе о анимации рассказывал о easing и одноименном плагине для jQuery? Так вот UI тоже расширяет easing, так что подключив его можно отключать плагин. И да, этот функционал завязан лишь на "Effects Core".

    Темы

    Одной из самых замечательных особенностей jQuery UI является возможность менять "шкурки" всех виджетов разом, и для этого даже предусмотрена специальная утилита – ThemeRoller [http://jqueryui.com/themeroller/]:

    Если в какой-то момент времени потребуется внести изменения в тему, то откройте файл jquery-ui-#.#.##-custom.css и найдёте строчку начинающуюся с текста "To view and modify this theme, visit http://..." и таки пройдите по указанной ссылке, и уже используя ThemeRoller внесите необходимые изменения.

    Пишем свой виджет

    Отправной точкой при написания виджета для jQuery UI для вас будет официальная документация, но поскольку со знанием английского не у всех сложилось, то я постараюсь перевести и адаптировать информацию изложенную в ней.

    Первое, о чём стоит рассказать, это то, что правила написания плагинов для jQuery слишком вальяжны, что не способствует их качеству. При создании jQuery UI, походу, решили пойти путём стандартизации процесса написания плагинов и виджетов, я не могу сказать насколько задумка удалась, но стало явно лучше чем было. Начну с описания каркаса для вашего виджета:

    $.widget("book.expose", {
    // настройки по умолчанию
    options: {
    color: "red"
    },
    // инициализация widget
    // вносим изменения в DOM и вешаем обработчики
    _create: function() {
    this.element; // искомый объект в jQuery обёртке
    this.name; // имя - expose
    this.namespace; // пространство – book
    this.element.on("click."+this.eventNamespace, function(){
    console.log("click");
    })
    },
    // метод отвечает за применение настроек
    _setOption: function( key, value ) {
    // применяем изменения настроек
    this._super("_setOption", key, value );
    },
    // метод _destroy должен быть антиподом к _create
    // он должен убрать все изменения внесенные изменения в DOM
    // и убрать все обработчики, если таковые были
    _destroy: function() {
    this.element.unbind('.'+this.eventNamespace);
    }
    });

    Поясню для тех кто не прочёл комментарии:

  • options – хранилище настроек виджета для конкретного элемента
  • _create() – отвечает за инициализацию виджета – тут должны происходить изменения в DOM'е, и "вешаться" обработчики событий
  • _destroy() – антипод для "_create()" – должен подчистить всё, что мы намусорили
  • _setOption(key, value) – данный метод будет вызван при попытке изменить какие-либо настройки:
  • $("#my").expose({key:value})

    Наблюдательный глаз заметит, что все перечисленные методы начинаются со знака подчёркивания – это такой способ выделить "приватные" методы, которые недоступны для запуска, и если мы попытаемся запустить "$('#my').expose('_destroy')", то получим ошибку. Но учтите – это лишь договорённость, соблюдайте, её!

    Для обхода договорённости о приватности можно использовать метод "data()":

    $("#my").data("expose")._destroy() // место для смайла "(evil)"

    В данном примере, я постарался задать хороший тон написания виджетов – я "повесил" обработчики событий в namespace, это даст в дальнейшем возможность контролировать происходящее без необходимости залазить в код виджета, это "true story".

    Код описанный в методе "_destroy()" – избыточен, т.к. он и так выполняется в публичном "destroy()", приведён тут для наглядности.

    А для ленивых, чтобы не прописывать каждый раз "eventNamespace" в обработчиках событий, разработчики добавили в версии 1.9.0 два метода: "_on()" и "_off()", первый принимает два параметра:

  • DOM элемент, или селектор, или jQuery объект
  • набор обработчиков событий в виде объекта
  • Все перечисленные события будут "висеть" в пространстве "eventNamespace", т.е. результат будет предположительно одинаковым:

    this._on(this.element, {
    mouseover:function(event) {
    console.log("Hello mouse");
    },
    mouseout:function(event) {
    console.log("Bye mouse");
    }
    });

    Второй метод – "_off()" – позволяет выборочно отключать обработчики:

    this._off(this.element, "mouseout click");

    Ну каркас баркасом, пора переходить к функционалу – добавим произвольную функцию с произвольным функционалом:

    callMe:function(){
    console.log("Allo?");
    }

    К данной функции мы легко сможем обращаться как из других методов виджета так и извне:

    // изнутри
    this.callMe()
    // извне
    $("#my").expose("callMe")

    Если ваша функция принимает параметры, то передача оных осуществляется следующим способом:

    $("#my").expose("callMe", "Hello!")

    Если вы хотите достучаться в обработчике событий до метода виджета, то не забудьте про область видимости переменных, и сделайте следующий манёвр:

    _create: function() {
    var self = this; // вот он!
    this.element.on("click."+this.eventNamespace, function(){
    // тут используем self, т.к. this уже указывает на
    // элемент по которому кликаем
    self.callMe();
    })
    },

    Хорошо идём, теперь поговорим о событиях – для более гибкой разработки и внедрения виджетов предусмотрен функционал по созданию произвольных событий и их "прослушиванию":

    // инициируем событие
    this._trigger("incomingCall");
    // подписываемся на событие при инициализации виджета
    $("#my").expose({
    incommingCall: function(ev) {
    console.log("din-don");
    }
    })
    // или после, используя в качестве имени события
    // имя виджета + имя события
    $("#my").bind("exposeincomingCall", function(){
    console.log("tru-lya-lya")
    });

    Материала много, я понимаю, но ещё добавлю описание нескольких методов которые можно вызвать из самого виджета:

    _delay() – данная функция работает как "setTimeout()", вот только контекст переданной функции будет указывать на сам виджет (это чтобы не заморачиваться с областью видимости)

    _hoverable() и _focusable() – данным методам необходимо скармливать элементы для которых необходимо отслеживать события "hover" и "focus", чтобы автоматически добавит к ним классы "ui-state-hover" и "ui-state-focus" при наступлении оных

    _hide() и _show() – эти два метода появились в версии 1.9.0, они созданы дабы стандартизировать поведение виджетов при использовании методов анимации, настройки принято прятать в опциях под ключами "hide" и "show" соответственно. Использовать методы следует следующим образом:

    options: {
    hide: {
    effect: "slideDown", // настройки эквиваленты вызову
    duration: 500 // .slideDown( 500)
    }
    }
    // внутри виджета следует использовать вызовы _hide() и _show()
    this._hide( this.element, this.options.hide, function() {
    // это наша функция обратного вызова
    console.log('спрятали');
    });

    Существует ещё пару методов, которые реализованы за нас:

    enable: function() {
    return this._setOption( "disabled", false );
    },
    disable: function() {
    return this._setOption( "disabled", true );
    },

    Фактически, данный функции создают синоним для вызова:

    $("#my").expose({ "disabled": true }) // или false

    Наша задача сводится лишь к отслеживанию данного флага в методе "_setOption()".

    Примеру быть, возможно этот виджет и не будет популярен, зато он наглядно демонстрирует как создавать виджеты для jQuery UI.

    <!DOCTYPE html>
    <html dir="ltr" lang="en-US">
    <head>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Пример widget'а для jQuery UI</title>
        <link rel="profile" href="http://gmpg.org/xfn/11"/>
        <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/>
        <link rel="stylesheet" href="css/styles.css"/>
        <style>
            .expose-widget {
                position: absolute;
                z-index: 999;
                background-color: rgba(0, 0, 0, 0.42);
                display: none;
            }
        </style>
        <script type="text/javascript" src="js/jquery.js"></script>
        <script type="text/javascript" src="js/jquery-ui.js"></script>
        <script type="text/javascript" src="js/code.js"></script>
    	<script>
            (function( $, undefined ) {
                $.widget( "book.expose", {
                    version: "1.0.0",
                    // наши настройки
                    options: {
                        color: null, // цвет фона
                        speed: 500,  // скорость анимации
                        show: 500,   // настройки для метода _show()
                        hide: {      // настройки для метода _hide()
                            effect: "fade",
                            duration: 500
                        }
                    },
                    // инициализация
                    _create: function() {
                        var self = this;
                        // выбранному элементу добавляем наш класс
                        // и вешаем обработчик события "клик"
                        this.element
                            .addClass( "book-widget" )
                            .on('click.expose-widget.'+this.eventNamespace, function() {
                                // вызываем метод виджета
                                // виджет доступен в переменной self
                                if (self.element.data('expose-it')) {
                                    self.unmask();
                                } else {
                                    self.mask();
                                }
                                return false;
                            });
                        // нам необходимо инициировать окружение лишь один раз для всех элементов
                        // это наш флаг
                        var totalInited = $.data(document.body, 'expose');
                        if (!totalInited) {
                            // изменяем флаг - у нас уже запущена инициализация
                            $.data(document.body, 'expose', 1);
                            // инициализация верхнего "бокса"
                            // и добавление в DOM
                            var $topBox = $('<div></div>');
                                $topBox.addClass("expose-widget");
                                $topBox.appendTo(document.body);
                            // сохраняем на него ссылку в реестре
                            $.data(document.body, 'topBox',    $topBox);
                            // клонируем верхний бокс
                            // добавляем и сохраняем
                            var $bottomBox = $topBox.clone();
                                $bottomBox.appendTo(document.body);
                            $.data(document.body, 'bottomBox', $bottomBox);
                            // повторяем для левого
                            var $leftBox = $topBox.clone();
                                $leftBox.appendTo(document.body);
                            $.data(document.body, 'leftBox',   $leftBox);
                            // и для правого
                            var $rightBox = $topBox.clone();
                                $rightBox.appendTo(document.body);
                            $.data(document.body, 'rightBox',  $rightBox);
                            // все боксы разом
                            $allBox = $topBox.add($bottomBox).add($leftBox).add($rightBox);
                            $.data(document.body, 'allBox',  $allBox);
                            // вешаем единый обработчик события для всех боксов
                            $(document.body).on('click.expose-widget', '.expose-widget', function() {
                                // вызов метода виджета
                                self.unmask();
                                return false;
                            });
                            // кидаем событие, на него можно подписаться лишь при инициализации виджета
                            // иначе толку не будет - будет поздно
                            this._trigger('build');
                        } else {
                            // отслеживаем кол-во виджетов
                            totalInited++;
                            $.data(document.body, 'expose', totalInited);
                        }
                        // вызываем метод виджета для применения цвета
                        this._setCurrentColor();
                    },
                    // ломаем всё что строили
                    _destroy: function() {
                        // подчищаем наш элемент
                        this.element
                            .removeClass( "book-widget" )
                            // данный метод избыточен, т.к. это делает за нас публичный метод destroy()
                            .off('.'+this.eventNamespace);
                        // декрементим кол-во инициализированных виджетов
                        var totalInited = $.data(document.body, 'expose');
                        totalInited--;
                        $.data(document.body, 'expose', totalInited);
                        // если больше нет виджетов
                        // удаляем вспомогательные боксы
                        if (totalInited==0) {
                            $.data(document.body, 'allBox').remove();
                            $(document.body).off('.expose-widget');
                            this._trigger('destroy');
                        }
                    },
                    // применяем настройки
                    _setOption: function( key, value ) {
                        if (key == 'disabled') {
                            // тут должен быть код по отключению виджета
                        }
                        if (key == 'speed') {
                            this.options.show = value;
                            this.options.hide.duration = value;
                        }
                        this._super( key, value );
                    },
                    // применяем настройки цвета
                    _setCurrentColor: function() {
                        this.options.color = $.data(document.body, 'topBox').css('background-color');
                    },
                    // изменяем цвет
                    color: function(value) {
                        $.data(document.body, 'allBox').css('background-color', value);
                        this._setCurrentColor();
                    },
                    // прячем боксы
                    unmask: function() {
                        var self = this;
                        // получаем набор
                        var $allBox = $.data(document.body, 'allBox');
                        // вызываем функцию анимации
                        this._hide($allBox, this.options.hide, function() {
                            // бросаем событие по завершению
                            self._trigger('unmask')
                        });
                        this._resetFlag();
                        // изменяем флаг
                        this.element.data('expose-it', false);
                    },
                    // показываем боксы
                    mask: function() {
                        var self = this;
                        // берём все "боксы"
                        var $allBox = $.data(document.body, 'allBox');
                        var $topBox = $.data(document.body, 'topBox');
                        var $bottomBox = $.data(document.body, 'bottomBox');
                        var $leftBox = $.data(document.body, 'leftBox');
                        var $rightBox = $.data(document.body, 'rightBox');
                        // вычисляем текущие координаты элемента и размеры окна
                        var offset = this.element.offset();
                        var height = this.element.outerHeight();
                        var width  = this.element.outerWidth();
                        var winWidth = $(document).width();
                        var winHeight = $(document).height();
                        var topSettings = {
                            top:0,
                            left:0,
                            width:winWidth,
                            height:offset.top,
                            backgroundColor:this.options.color
                        };
                        var leftSettings = {
                            top:offset.top,
                            left:0,
                            width:offset.left,
                            height:height,
                            backgroundColor:this.options.color
                        };
                        var rightSettings = {
                            top:offset.top,
                            left:offset.left+width,
                            width:winWidth - (offset.left+width),
                            height:height,
                            backgroundColor:this.options.color
                        };
                        var bottomSettings = {
                            top:offset.top+height,
                            left:0,
                            width:winWidth,
                            height:winHeight - (offset.top+height),
                            backgroundColor:this.options.color
                        };
                        // проверяем - отображается у нас боксы в данный момент времени или нет
                        if ($topBox.is(':hidden')) {
                            // применяем настройки
                            $topBox.css(topSettings);
                            $leftBox.css(leftSettings);
                            $rightBox.css(rightSettings);
                            $bottomBox.css(bottomSettings);
                            // показываем все наши боксы
                            this._show( $allBox, this.options.show, function() {
                                // событие mask
                                self._trigger('mask');
                            });
                        } else {
                            this._resetFlag();
                            // применяем настройки через анимацию
                            $topBox.animate(topSettings, this.options.speed);
                            $leftBox.animate(leftSettings, this.options.speed);
                            $rightBox.animate(rightSettings, this.options.speed);
                            $bottomBox.animate(bottomSettings, this.options.speed, function(){
                                // событие mask
                                self._trigger('mask');
                            });
                        }
                        // изменяем флаг
                        this.element.data('expose-it', true);
                    },
                    _resetFlag:function() {
                        // workaround - надо "сбросить" флаг для всех других элементов
                        // надо будет придумать более красивое решение
                        $.each($.cache, function(i, el) {
                            if (el.data != undefined  el.data.exposeIt != undefined) {
                                el.data.exposeIt = false;
                            }
                        });
                    }
                });
            })( jQuery );
    	</script>
    </head>
    <body>
        <div id="content" class="wrapper box">
            <menu label="Try...">
    			<a href="tabs.html" title="go prev" class="button alignleft" rel="prev">← Prev </a>
                <a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a>
                <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a>
                <hr/>
                <pre><code><em>// run widget and try to click on article</em>
    $(<span>'article'</span>).expose()</code></pre>
                <button type="button" class="code">Run Code</button>
                <pre><code contenteditable="true"><em>// configuration was changed</em>
    $(<span>'article'</span>).expose({speed:2000})</code></pre>
                <button type="button" class="code">Run Code</button>
                <pre><code contenteditable="true"><em>// run widget and try to click on image</em>
    $(<span>'article img'</span>).expose({speed:100})</code></pre>
                <button type="button" class="code">Run Code</button>
                <pre><code><em>// try widget API - trigger</em>
    $(<span>'article:eq(0)'</span>).bind(<span>'exposemask'</span>,
        function(){
            $(this).css(<span>'color'</span>, <span>'red'</span>);
        });
    $(<span>'article:eq(0)'</span>).bind(<span>'exposeunmask'</span>,
        function(){
            $(this).css(<span>'color'</span>, <span>'black'</span>);
        })</code></pre>
                <button type="button" class="code">Run Code</button>
                <pre><code><em>// try widget API - call method</em>
    $(<span>'article:eq(1)'</span>).expose(<span>'mask'</span>)</code></pre>
                <button type="button" class="code">Run Code</button>
                <pre><code contenteditable="true"><em>// try widget API - call method+args</em>
    $(<span>'article:eq(1)'</span>).expose(
        <span>'color'</span>,
        <span>'rgba(255, 0, 0, 0.5)'</span>
    )</code></pre>
                <button type="button" class="code">Run Code</button>
            </menu>
            <header>
                <h1>widget</h1>
                <h2>Пишем своё виджет для jQuery UI</h2>
            </header>
            <article>
                <h2>Article</h2>
                <p>
                    <img src="images/photo-bumblebee-tumb.jpg" alt="Bumblebee" class="left" width="200"/>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rutrum,
                lectus eu varius consectetur, libero velit hendrerit augue, ut posuere enim neque
                in libero. Donec eget sagittis nibh. Suspendisse sed tincidunt urna. Cras quis
                euismod neque. Maecenas auctor ultricies posuere. Pellentesque luctus pulvinar dui
                eget semper. Donec sodales odio eu sapien varius luctus. Donec dictum feugiat diam
                at malesuada. Sed nec massa in augue condimentum faucibus quis ut diam. Quisque
                nisl sem, semper nec vulputate vel, mattis sit amet justo. Aliquam purus felis,
                tempor at scelerisque quis, tincidunt in neque. Etiam ut risus diam. Pellentesque
                fermentum risus id elit feugiat cursus. Ut fringilla dictum diam, sed iaculis
                lorem pulvinar ut. Cras vel elit id velit commodo viverra sit amet vel orci.</p>
            </article>
            <article>
                <h2>Article</h2>
                <p>
                    <img src="images/photo-chamomile-tumb.jpg" alt="Chamomile" class="left" width="200"/>
                Duis in vestibulum sem. Cras euismod tincidunt dui, et scelerisque tellus condimentum vel.
                Maecenas et urna sit amet risus fermentum rhoncus nec porttitor ligula. Maecenas sit amet
                turpis enim, ut iaculis est. Duis feugiat, lacus id placerat porttitor, lorem augue gravida
                nisi, eu porta eros risus et lectus. Maecenas vestibulum nunc vel ipsum tincidunt sit amet
                blandit sapien bibendum. Proin vel vulputate nisl. Duis tempor imperdiet placerat. Pellentesque
                faucibus consequat magna, et bibendum nisl egestas non. Pellentesque sit amet mattis augue.
                Aenean at diam tincidunt purus sollicitudin gravida non in nisi. Fusce bibendum, magna in
                adipiscing mattis, sem risus fringilla mi, nec gravida lectus lectus at nibh. Suspendisse
                adipiscing elementum laoreet. Suspendisse sem erat, varius quis aliquet vitae, dapibus sed
                nibh. Nullam iaculis sem at mauris faucibus in vestibulum libero pretium. Aliquam eu turpis
                libero. Fusce et ultrices lectus.</p>
            </article>
            <article>
                <h2>Article</h2>
                <p>
                    <img src="images/photo-maple-leaf-tumb.jpg" alt="Maple Leaf" class="left" width="200"/>
                Ut consequat commodo mauris, eu dignissim justo congue vel. Etiam commodo tincidunt diam,
                laoreet ullamcorper sapien egestas quis. Etiam auctor rutrum ante, at tincidunt elit lacinia
                non. Pellentesque molestie tellus sit amet est sodales nec rutrum leo pharetra. Donec lacinia
                ipsum vitae massa accumsan ullamcorper. Maecenas commodo lacus turpis. Proin sit amet mauris
                sem, imperdiet faucibus lorem. Fusce ullamcorper consectetur ligula vel pretium. Sed et elit
                vitae orci adipiscing condimentum id sed turpis. Morbi ultrices feugiat ullamcorper. Fusce at
                magna dolor. Sed sit amet risus massa, quis imperdiet libero. Proin justo purus, sodales nec
                cursus et, sollicitudin at nulla. Vivamus eget nibh tellus, sit amet facilisis ante.</p>
            </article>
            <footer>
                ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a>
            </footer>
            <script type="text/javascript">
                var _gaq = _gaq || [];
                _gaq.push(['_setAccount', 'UA-1669896-2']);
                _gaq.push(['_trackPageview']);
                (function() {
                 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
                 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
                })();
            </script>
    	</div>
    </body>
    </html>
    

    Будьте внимательны, с выходом jQuery UI версии 1.9.0 были внесены правки в Widget API, следовательно, большинство доступной инфор- мации устарело, так что читайте официальную документацию [http://wiki.jqueryui.com/w/page/12138135/Widget%20factory], а ещё лучше – заглядывайте в код готовых виджетов "от производителя"

    Информация по теме разработки виджетов:

  • "The jQuery UI Widget Factory. WAT?" – эта документации актуальна [http://ajpiano.com/widgetfactory/]
  • "Understanding jQuery UI widgets: A tutorial" [http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/]
  • "Tips for Developing jQuery UI 1.8 Widgets" [http://www.erichynds.com/jquery/tips-for-developing-jquery-ui-widgets/]
  • "Coding your First jQuery UI Plugin" [http://net.tutsplus.com/tutorials/javascript-ajax/coding-your-first-jquery-uiplugin/]
  • jQuery Tools

    Альтернатива jQuery UI, хотя я бы назвал полезным дополнением. Библиотека jQuery Tools состоит из компонентов, которые разделяются на три типа:

  • UI Tools – интерактивные компоненты интерфейса
  • Form Tools – компоненты по работе с формами
  • Toolbox – всяко-разно и полезно
  • UI Tools

    Tabs – аналогично jQuery UI по назначению, скромнее по функционалу, плюс еще есть возможность настраивать как "accordion":

    Tooltip – всплывающие подсказки, я не уверен в юзабельности оных, но может для кого-то данный компонент окажется полезным, ведь его так же включили в последний jQuery UI:

    Overlay – создание всплывающих модальных окошек, полезный компонент, аля "lightbox":

    Scrollable – компонент для создания "карусели" из картинок, может пригодиться в любом онлайн-магазине:

    Form Tools

    Validator – позволяет на декларативном уровне задавать правила проверки вводимых значений, что бывает очень полезно, и крайне удобно (совместим с HTML5).

    RangeInput – аналогичен jQuery UI Slider

    DateInput – аналогичен jQuery UI Datepicker

    Toolbox

    Expose – пожалуй самый замечательный компонент из всего набора – он позволяет выделять элемент на странице, путём затемнение остальных элементов; это трудно описать, лучше посмотрите на демо: http://jquerytools.org/demos/toolbox/expose/index.html

    Flashembed – компонент для быстрого и удобного встраивания Flash-объектов на страницу, прямой конкурент SWFObject

    Данный набор утилит достаточно долго не обновлялся, с марта 2012-го года, но он ещё не канул в лету, работа идёт, и обещают выпустить вторую версию.

    На этом, пожалуй, стоит закончить этот обзор.

    jQWidgets

    jQWidgets – это не просто библиотека, а целый комбайн с кучей полезных виджетов, да их на данный момент насчитывается тридцать четыре штуки, тут только привести список и скриншот на каждый – уже пол книги будет, так что обойдёмся кратеньким резюме на каждый виджет. Начну обзор с комплексных и "тяжёлых" виджетов, к ним картинки я таки приложу.

    jqxGrid – это датагрид с кучей полезных и не очень примочек, тут понятное дело есть постраничная навигация, сортировки, фильтрации и группировки, только по этому виджету уже можно здоровенный талмуд написать:

    jqxChart – виджет для построения разнообразных графиков с помощью HTML, CSS и JavaScript, сделано всё очень и очень культурно, особенно выделю функцию по сохранению графика как картинки, иногда очень её не хватает:

    jqxGauge – этот виджет не часто встретишь и в даже более именитых фреймворках, но по сути – это некий измеритель, т.е. с его помощью можно нарисовать спидометр, манометр, термометр или ещё какой другой измерительный прибор с произвольной шкалой:

    jqxTreeMap – ещё один редкий вид, скорее даже уникальный, с его помощью можно построить связанное дерево в виде организованных прямоугольничков, если ничего не понятно, то лучше просмотреть демку, ну и скриншот прилагаю:

    jqxTree – это уже не столь экзотический виджет, как понятно из названия, будем садить деревья:

    На этом обзор "крутых" виджетов можно заканчивать, углубляться в скучные и обыденный обёртки над элементами форм мне не хочется, замечу лишь, что во многом данный фреймворк обходит jQuery UI, но не всё так радужно в этом королевстве:

    Данный фреймворк распространяется под лицензией Creative Commons Attribution-NonCommercial 3.0 License, которая предусматри- вает бесплатное использование библиотеки для не коммерческих проектов, иначе – смотрите расценки.

    Ещё стоит упомянуть одну приятную особенность – это возможность легкой интеграции с MVVM фреймворком Knockout, но это уже другая история

    jQuery Mobile

    А вот это вполне самостоятельный продукт, и как следует из названия предназначен для создания интерфейсов для мобильных устройств с поддержкой "Touch Screen". Этот фреймворк хорошо документирован, с кучей примеров, которые можно пощупать на http://jquerymobile.com/

    Данный фреймворк хорошо подходит для создания мобильных версий сайтов, но при этом он будет выглядеть как мобильное приложение, хотя нет – сайт будет выглядеть как сайт на jQuery Mobile. Насколько это хорошо или плохо мне судить сложно, скажем так – это востребовано.

    Я не буду рассматривать все компоненты данного фреймворка, приведу лишь некоторые скриншоты (взяты с официального сайта http://jquerymobile.com/):

    Для jQMobile существует свой собственный ThemeRoller – http://jquerymobile.com/themeroller/

    И еще, посоветую обратить внимание на API данного фреймворка, без изучения оного вам будет затруднительно создать действительно интересные приложения.

    Еще плагины

    Хотелось бы порекомендовать еще несколько плагинов, которые стоит всегда держать под рукой:

    color — если потребуется анимация цвета фона, либо шрифта, либо еще чего-нибудь (если не хотите использовать jQuery UI) [https://github.com/jquery/jquery-color]

    cookie— удобная работа с "печеньками" в браузере [http://archive.plugins.jquery.com/project/Cookie]

    easing — расширяем стандартный набор функций easing (об этом я рассказывал в главе анимация) [http://gsgd.co.uk/sandbox/jquery/easing/]

    form — упрощает работу с формами, сам уже давно не пользуюсь, но для быстрого старта самое оно[http://malsup.com/jquery/form/]

    hotkeys — название говорит само за себя [https://github.com/jeresig/jquery.hotkeys]

    mouswheel — добавляет к jQuery возможность отслеживать события колесика мышки (да, да, из коробки этого функционала нет в библиотеке) [http://brandonaaron.net/code/mousewheel/docs]

    profiler – как и следует из названия – помогает отлаживать код jQuery приложений [http://archive.plugins.jquery.com/project/profile] [http://ejohn.org/blog/deep-profiling-jquery-apps/]

    shadow animation — как следует из названия – плагин для анимации теней [http://www.bitstorm.org/jquery/shadow-animation/]

    jQuery Transition Events – поддержка CSS Transition из JS [https://github.com/ai/transition-events]

    SWFObject — если потребуется вставить какую-нибудь flash’ку на страницу, данный плагин облегчит вам участь[http://jquery.thewikies.com/swfobject/]

    SWFObject — если потребуется вставить какую-нибудь flash’ку на страницу, данный плагин облегчит вам участь[http://jquery.thewikies.com/swfobject/]

    Обновление на версию 2.х

    Чем же нам грозит новая версия? Ну нам то боятся её не стоит, а вот пользователи старых версий Internet Explorer не смогут больше насладиться удобством столь популярной библиотеки. Да, да, именно так, из второй версии убрали поддержку IE с 6-ой по 8-ую. Благодаря этой "оптимизации" библиотека похудела на 10%.

    Если более современные IE работают в режиме совместимости со старыми версиями, то jQuery не будет заморачиваться, и откажется работать. Избежать это поможет мета-тег X-UA-Compatible или заголовок HTTP.

    В скором времени "под нож" пойдут и другие устаревающие браузеры, будьте бдительны Android/WebKit 2.x, за вами уже выехали.

    Если вас не зацепила эта диета, то можно пойти дальше, и собрать jQuery только из того, что вам действительно необходимо, что для этого потребуется – читайте в подробном пошаговом руководстве, которому и перевод-то не требуется.

    Если вас не впечатляет этот список измений, то это лишь благодаря труду разработчиков jQuery, которые постарались обеспечить полную совместимость API веток 1.х и 2.х, а вот это уже действительно впечатляет.

    Ну ладно, не всё так гладко с совместимостью, был этот путь тернист, и проблемы с обновлением будут, вот только искать причину нужно будет в списке изменений версии 1.9, именно эта версия стала поворотной в вопросе совместимости веток.

  • "How to build your own jQuery"[https://github.com/jquery/jquery/]
  • "jQuery Core 1.9 Upgrade Guide" [http://jquery.com/upgrade-guide/1.9/]
  • История изменений

    1.0.0 alpha Первая публикация "для своих", всего 75 скачиваний, вычитка началась
    1.0.0 beta Исправление опечаток и грамматических ошибок. Публикация на Хабре и более 13 000 скачиваний
    1.0.0 Релиз :) Исправлено множество опечаток и несколько ссылок. Добавлена информация о быстродействии селекторов вида "div#id" – спасибо хабрапользователю skorney за тестирование. Более 2 000 скачиваний
    1.0.1 Ещё несколько мелких правок. Спасибо читателям за отзывы. 4500+
    1.0.2 И ещё… Опубликовал в ePub формате, выложил на scribd, получилось более 8 000 скачиваний и более 10 000 прочтений на scribd
    1.0.3 Обновил книгу вслед за обновлением jQuery, добавил информацию о изменениях в версии 2.х относительно 1.х. Добавил описание виджетов jQWidgets. Удалил описание событий для метода data(). Примеры кода используемые в учебнике теперь доступны на GitHub, так что жду pull-запросов – https://github.com/AntonShevchuk/jquery-for-beginners. Счётчик прочтений на scribd перевалил за 20 тысяч, при этом версию 1.0.2 скачали более 10 тысяч раз…
    1.0.4 Оптимизация шрифтов под печать формата А5. Ушла в тираж.
    1.0.5 Ждёмс…
    Страницы:

    Дополнение

    Еще чуть-чуть о полезном инструментарии: есть такой классный плагин – jQuery-inlog [http://prinzhorn.github.com/jquery-inlog/] – основное его назначение — дать нам чуть-чуть больше понимания о происходящем внутри самого jQuery, вот кусочек HTML:

    <body>
    <div class="bar">
    <div class="bar">
    <div id="foo"></div> 
    </div>
    </div>
    <div id="bacon"></div> </body>
    

    А вот и код, который его обслуживает:

    $l(true);
    $("#foo").parents(".bar").next().prev().parent().fadeOut();
    $l(false);

    Какие-то странные манипуляции, для какого же элемента будет применён метод "fadeout()"? Для выяснения оного наш код обёрнут в вызов метода "$l()". "$l()" — это и есть собственно вызов плагина, результат его работы можно найти в консоли:

    У данного плагина есть ещё настройки, которые регулируют объём информации выводимой в консоль.

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

    jQuery UI

    jQuery UI представляет из себя набор виджетов и плагинов от самих разработчиков jQuery. По моему мнению, данный инструмент необходимо изучить настолько, насколько это требуется чтобы не писать свои "велосипеды". Скачать-почитать о данной надстройке над jQuery можно на домашней страницы проекта – http://jqueryui.com/

    Что нам необходимо знать о виджетах и плагинах? Первое – это какие они есть, и второе – как работают. На этих двух моментах я и постараюсь остановиться.

    Интерактивность

    Начну с полезных плагинов, которые могут упростить жизнь при создании интерактивных интерфейсов:

  • Draggable [http://jqueryui.com/position/] – данный компонент позволяет сделать любой DOM элемент перетаскиваемым при помощи мыши
  • Droppable [http://jqueryui.com/droppable/] – это логичное продолжение draggable компонента, необходим для работы с контейнерами, внутрь которых можно перетаскивать элементы
  • Resizable [http://jqueryui.com/resizable/] – как следует из название – даёт возможность растягивать любые DOM элементы
  • Selectable [http://jqueryui.com/selectable/] – позволяет организовать "выбор" элементов, удобно использовать для организации менеджмента картинок
  • Sortable [http://jqueryui.com/sortable/] – сортировка DOM элементов
  • Виджеты

    Виджеты – это уже комплексное решение содержащие не только JavaScript код, но и некую HTML и CSS реализацию:

  • Accordion – данный виджет следует использовать если у вас уже используется jQuery UI в проекте, сам по себе основной его функционал можно реализовать в несколько строк:
    <!DOCTYPE html>
    <html dir="ltr" lang="en-US">
    <head>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Accordion</title>
        <link rel="profile" href="http://gmpg.org/xfn/11"/>
        <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/>
        <link rel="stylesheet" href="css/styles.css"/>
        <script type="text/javascript" src="js/jquery.js"></script>
        <script type="text/javascript" src="js/code.js"></script>
        <script type="text/javascript">
            $(function(){
                var toDisplay = 1;
                $("article h2").click(function(){
                    var $this = $(this);
                    $this
                        .next("p")
                            .slideToggle("slow")
                        .siblings("p:visible")
                            .slideUp("slow");
                    $this.toggleClass("active");
                    $this.siblings("h2").removeClass("active");
                })
                    .eq(toDisplay).addClass("active")
                    .next().show();
            });
        </script>
        <style type="text/css">
            article h2 {
                background: #e9e7e7 url(images/arrows.png) no-repeat 618px 7px;
                padding: 7px 15px;
                margin: 0;
                border: solid 1px #c4c4c4;
                border-bottom: none;
                cursor: pointer;
            }
            article h2:hover {
                background-color: #e3e2e2;
            }
            article h2.active {
                background-position: 618px -23px;
            }
            article p {
                display: none;
                margin: 0;
                padding:  10px 15px 20px;
            }
        </style>
    </head>
    <body>
        <div id="content" class="wrapper box">
            <menu>
                <a href="index.html" title="go prev" class="button alignleft" rel="prev">← Back </a>
                <a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a>
                <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a>
                <a href="tabs.html" title="go next" class="button alignright" rel="next">Next →</a>
            </menu>
            <header>
               <h1>Реализация аккордиона</h1>
               <h2>На jQuery без использования jQuery UI</h2>
            </header>
            <article>
                <h2>Article Title</h2>
                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rutrum,
                lectus eu varius consectetur, libero velit hendrerit augue, ut posuere enim neque
                in libero. Donec eget sagittis nibh. Suspendisse sed tincidunt urna. Cras quis
                euismod neque. Maecenas auctor ultricies posuere. Pellentesque luctus pulvinar dui
                eget semper. Donec sodales odio eu sapien varius luctus. Donec dictum feugiat diam
                at malesuada. Sed nec massa in augue condimentum faucibus quis ut diam. Quisque
                nisl sem, semper nec vulputate vel, mattis sit amet justo. Aliquam purus felis,
                tempor at scelerisque quis, tincidunt in neque. Etiam ut risus diam. Pellentesque
                fermentum risus id elit feugiat cursus. Ut fringilla dictum diam, sed iaculis
                lorem pulvinar ut. Cras vel elit id velit commodo viverra sit amet vel orci.</p>
                <h2>Article Title</h2>
                <p>Duis in vestibulum sem. Cras euismod tincidunt dui, et scelerisque tellus condimentum vel.
                Maecenas et urna sit amet risus fermentum rhoncus nec porttitor ligula. Maecenas sit amet
                turpis enim, ut iaculis est. Duis feugiat, lacus id placerat porttitor, lorem augue gravida
                nisi, eu porta eros risus et lectus. Maecenas vestibulum nunc vel ipsum tincidunt sit amet
                blandit sapien bibendum. Proin vel vulputate nisl. Duis tempor imperdiet placerat. Pellentesque
                faucibus consequat magna, et bibendum nisl egestas non. Pellentesque sit amet mattis augue.
                Aenean at diam tincidunt purus sollicitudin gravida non in nisi. Fusce bibendum, magna in
                adipiscing mattis, sem risus fringilla mi, nec gravida lectus lectus at nibh. Suspendisse
                adipiscing elementum laoreet. Suspendisse sem erat, varius quis aliquet vitae, dapibus sed
                nibh. Nullam iaculis sem at mauris faucibus in vestibulum libero pretium. Aliquam eu turpis
                libero. Fusce et ultrices lectus.</p>
                <h2>Article Title</h2>
                <p>Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
                Morbi dapibus, dui in consequat pulvinar, nunc erat consequat ipsum, ut blandit nulla eros ac purus.
                In at justo sed lorem consequat fermentum. Nunc scelerisque purus sed neque rutrum adipiscing. Sed
                vehicula tristique dolor, quis porttitor magna dictum eu. Cras tristique erat non orci aliquam tempor.
                In hac habitasse platea dictumst. Nam eget sodales neque. Aenean adipiscing consequat diam, eu
                molestie diam dictum eu.</p>
            </article>
            <footer>
               ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a>
            </footer>
            <script type="text/javascript">
                var _gaq = _gaq || [];
                _gaq.push(['_setAccount', 'UA-1669896-2']);
                _gaq.push(['_trackPageview']);
                (function() {
                 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
                 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
                })();
            </script>
        </div>
    </body>
    </html>
  • Autocomplete – как и следует из название, данный виджет отвечает за добавление функции автодополнения к полям ввода, естественно с поддержкой AJAX
  • Button – создание кнопок используя JavaScript – ещё тот моветон, но возможно пригодится, если вы сильно завязались на jQuery UI:
  • Datepicker – если ваш браузер не поддерживает в полной мере спецификацию HTML5 и <input type="date"/> в частности, то потребуется эмуляция данной возможности с помощью виджета datepicker:
  • Dialog – виджет предназначенный для создания слегка неуклюжих диалоговых окон:
  • Menu – создание меню из списка, с поддержкой вложенности
  • Progressbar – название говорит само за себя, и да в HTML5 он тоже включён:
  • Slider – ещё один виджет для устаревших браузеров:
  • Spinner – ещё один удобный контрол для форм, опять же – в HTML5 уже есть:
  • Tabs – они же табы – достаточно популярный элемент в web-разработке, и так же как и "accordion" вполне заменяем простым кодом
    <!DOCTYPE html>
    <html dir="ltr" lang="en-US">
    <head>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Tabs</title>
        <link rel="profile" href="http://gmpg.org/xfn/11"/>
        <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/>
        <link rel="stylesheet" href="css/styles.css"/>
        <script type="text/javascript" src="js/jquery.js"></script>
        <script type="text/javascript" src="js/code.js"></script>
        <script type="text/javascript">
            $(function(){
                var hash = window.location.hash;
                $("article li a").click(function(){
                    var $this = $(this);
                    var $pan = $("article").find($this.attr('href'));
                    if (!$this.hasClass('active')) {
                        $("article li a").removeClass("active");
                        $("article p").hide();
                        $this.addClass("active");
                        $pan.show();
                        window.location.hash = $this.attr('href');
                    }
                    return false;
                });
                if (hash  $("article li a[href="+hash+"]").length) {
                    $("article li a[href="+hash+"]").click();
                } else {
                    $("article li a").eq(0).click();
                }
            });
        </script>
        <style type="text/css">
            article ul {
                padding: 0 15px 0;
                margin-bottom: -1px;
                overflow: hidden;
            }
            article li {
                float:left;
                list-style: none;
                margin: 0 4px;
            }
            article li a {
                display: block;
                padding: 4px 8px;
                background: #f5f5f5;
                font-size: 14px;
                border: 1px solid #ccc;
                border-radius: 2px 2px 0 0;
            }
            article p {
                clear: both;
                display: none;
                margin: 0 4px;
                padding:  10px 15px 20px;
                border: 1px solid #ccc;
                border-radius: 2px;
            }
            article a.active {
                background: #fff;
                border-bottom: 1px solid #fff;
            }
        </style>
    </head>
    
    <body>
    <div id="content" class="wrapper box">
           <menu>
               <a href="accordion.html" title="go prev" class="button alignleft" rel="prev">← Prev </a>
               <a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a>
               <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a>
               <a href="widget.html" title="go next" class="button alignright" rel="next">Next →</a>
           </menu>
           <header>
               <h1>Реализация закладок</h1>
               <h2>На jQuery без использования jQuery UI</h2>
           </header>
           <article>
               <ul>
                   <li><a href="#pan1">First Article</a></li>
                   <li><a href="#pan2">Second Article</a></li>
                   <li><a href="#pan3">Third Article</a></li>
               </ul>
    
               <p id="pan1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rutrum,
               lectus eu varius consectetur, libero velit hendrerit augue, ut posuere enim neque
               in libero. Donec eget sagittis nibh. Suspendisse sed tincidunt urna. Cras quis
               euismod neque. Maecenas auctor ultricies posuere. Pellentesque luctus pulvinar dui
               eget semper. Donec sodales odio eu sapien varius luctus. Donec dictum feugiat diam
               at malesuada. Sed nec massa in augue condimentum faucibus quis ut diam. Quisque
               nisl sem, semper nec vulputate vel, mattis sit amet justo. Aliquam purus felis,
               tempor at scelerisque quis, tincidunt in neque. Etiam ut risus diam. Pellentesque
               fermentum risus id elit feugiat cursus. Ut fringilla dictum diam, sed iaculis
               lorem pulvinar ut. Cras vel elit id velit commodo viverra sit amet vel orci.</p>
               <p id="pan2">Duis in vestibulum sem. Cras euismod tincidunt dui, et scelerisque tellus condimentum vel.
               Maecenas et urna sit amet risus fermentum rhoncus nec porttitor ligula. Maecenas sit amet
               turpis enim, ut iaculis est. Duis feugiat, lacus id placerat porttitor, lorem augue gravida
               nisi, eu porta eros risus et lectus. Maecenas vestibulum nunc vel ipsum tincidunt sit amet
               blandit sapien bibendum. Proin vel vulputate nisl. Duis tempor imperdiet placerat. Pellentesque
               faucibus consequat magna, et bibendum nisl egestas non. Pellentesque sit amet mattis augue.
               Aenean at diam tincidunt purus sollicitudin gravida non in nisi. Fusce bibendum, magna in
               adipiscing mattis, sem risus fringilla mi, nec gravida lectus lectus at nibh. Suspendisse
               adipiscing elementum laoreet. Suspendisse sem erat, varius quis aliquet vitae, dapibus sed
               nibh. Nullam iaculis sem at mauris faucibus in vestibulum libero pretium. Aliquam eu turpis
               libero. Fusce et ultrices lectus.</p>
               <p id="pan3">Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
               Morbi dapibus, dui in consequat pulvinar, nunc erat consequat ipsum, ut blandit nulla eros ac purus.
               In at justo sed lorem consequat fermentum. Nunc scelerisque purus sed neque rutrum adipiscing. Sed
               vehicula tristique dolor, quis porttitor magna dictum eu. Cras tristique erat non orci aliquam tempor.
               In hac habitasse platea dictumst. Nam eget sodales neque. Aenean adipiscing consequat diam, eu
               molestie diam dictum eu.</p>
           </article>
           <footer>
               ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a>
           </footer>
            <script type="text/javascript">
                var _gaq = _gaq || [];
                _gaq.push(['_setAccount', 'UA-1669896-2']);
                _gaq.push(['_trackPageview']);
                (function() {
                 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
                 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
                })();
            </script>
    </div>
    </body>
    </html>
  • Tooltip – вот и последний виджет – всплывающие подсказки, простой и должен быть востребован, ну а там жизнь покажет
  • На этом обзор виджетов можно считать законченным, вернёмся к плагинам.

    Все виджеты и плагины завязаны на ядро jQuery UI, но есть так же зависимости между самими плагинами и стоит о них помнить. Но не переживайте – при сборке jQuery UI пакета все зависимости проверяются автоматически, т.е. когда вам потребуется доселе неподключенный виджет, лучше скачать сборку заново.

    Утилиты

    Утилит у нас не много – вот полезный плагин – position, который позволяет контролировать положение DOM элементов – http://jqueryui.com/position/, а ещё есть фабрика по созданию виджетов, но о ней я расскажу чуть попозже.

    Эффекты

    Среди эффектов предоставляемых jQuery UI я выделяю четыре пункта:

  • Анимация цвета
  • Анимация изменения классов
  • Набор эффектов
  • Расширения возможностей easing
  • За анимацию цвета отвечает компонент "Effects Core", который позволяет анимировать изменения цвета посредством использования функции "animate()":

    $("#my").animate({ backgroundColor: "black" }, 1000);

    Да-да, jQuery из коробки не умеет этого делать, а вот jQuery UI позволяет анимировать следующие параметры:

  • backgroundColor
  • borderBottomColor
  • borderLeftColor
  • borderRightColor
  • borderTopColor
  • color
  • outlineColor
  • Ещё одной возможностью заключенной в "Effects Core" является анимация изменений класса DOM элемента, т.е. когда вы будете присваивать новый класс элементу, то вместо обычного моментального применения новых CSS свойств вы будете наблюдать анимацию этих свойств от текущих до заданных в присваиваемом классе. Для использования данного функционала нам потребуются старые знакомые – методы "addClass()", "toggleClass()" и "removeClass()", с одной лишь разницей – при вызове метода вторым параметром должна быть указана скорость анимации:

    $("#my").addClass("active", 1000);
    $("#my").toggleClass("active", 1000);
    $("#my").removeClass("active", 1000);

    Если из предыдущего абзаца у вас не возникло понимания сути происходящего, то этот код для вас:

    <style>
    #my {
    font-size:14px;
    }
    #my.active {
    font-size:20px;
    }
    </style>
    <script>
    $(function (){
    $("#my").addClass("active", 1000);
    // тут получается аналогично следующему вызову
    $("#my").animate({"font-size":"20px"}, 1000);
    });
    </script>

    А ещё появляется метод switchClass(removeClass, addClass, duration), который заменяет один класс другим, но мне он ни разу не пригодился.

    О наборе эффектов я не буду долго рассказывать, их лучше посмотреть в действии на странице http://jqueryui.com/effect/. Для работы с эффектами появляется метод "effect()", но сам по себе его лучше не использовать, ведь UI расширил функционал встроенных методов "show()", "hide()" и "toggle()", теперь, передав в качестве параметра скорости анимации названия эффекта вы получите необходимый результат:

    $("#my").hide("puff");
    $("#my").show("transfer");
    $("#my").toggle("explode");

    Приведу список эффектов, может кто запомнит: blind, bounce, clip, drop, explode, fold, highlight, puff, pulsate, scale, shake, size, slide, transfer.

    Помните, я в главе о анимации рассказывал о easing и одноименном плагине для jQuery? Так вот UI тоже расширяет easing, так что подключив его можно отключать плагин. И да, этот функционал завязан лишь на "Effects Core".

    Темы

    Одной из самых замечательных особенностей jQuery UI является возможность менять "шкурки" всех виджетов разом, и для этого даже предусмотрена специальная утилита – ThemeRoller [http://jqueryui.com/themeroller/]:

    Если в какой-то момент времени потребуется внести изменения в тему, то откройте файл jquery-ui-#.#.##-custom.css и найдёте строчку начинающуюся с текста "To view and modify this theme, visit http://..." и таки пройдите по указанной ссылке, и уже используя ThemeRoller внесите необходимые изменения.

    Пишем свой виджет

    Отправной точкой при написания виджета для jQuery UI для вас будет официальная документация, но поскольку со знанием английского не у всех сложилось, то я постараюсь перевести и адаптировать информацию изложенную в ней.

    Первое, о чём стоит рассказать, это то, что правила написания плагинов для jQuery слишком вальяжны, что не способствует их качеству. При создании jQuery UI, походу, решили пойти путём стандартизации процесса написания плагинов и виджетов, я не могу сказать насколько задумка удалась, но стало явно лучше чем было. Начну с описания каркаса для вашего виджета:

    $.widget("book.expose", {
    // настройки по умолчанию
    options: {
    color: "red"
    },
    // инициализация widget
    // вносим изменения в DOM и вешаем обработчики
    _create: function() {
    this.element; // искомый объект в jQuery обёртке
    this.name; // имя - expose
    this.namespace; // пространство – book
    this.element.on("click."+this.eventNamespace, function(){
    console.log("click");
    })
    },
    // метод отвечает за применение настроек
    _setOption: function( key, value ) {
    // применяем изменения настроек
    this._super("_setOption", key, value );
    },
    // метод _destroy должен быть антиподом к _create
    // он должен убрать все изменения внесенные изменения в DOM
    // и убрать все обработчики, если таковые были
    _destroy: function() {
    this.element.unbind('.'+this.eventNamespace);
    }
    });

    Поясню для тех кто не прочёл комментарии:

  • options – хранилище настроек виджета для конкретного элемента
  • _create() – отвечает за инициализацию виджета – тут должны происходить изменения в DOM'е, и "вешаться" обработчики событий
  • _destroy() – антипод для "_create()" – должен подчистить всё, что мы намусорили
  • _setOption(key, value) – данный метод будет вызван при попытке изменить какие-либо настройки:
  • $("#my").expose({key:value})

    Наблюдательный глаз заметит, что все перечисленные методы начинаются со знака подчёркивания – это такой способ выделить "приватные" методы, которые недоступны для запуска, и если мы попытаемся запустить "$('#my').expose('_destroy')", то получим ошибку. Но учтите – это лишь договорённость, соблюдайте, её!

    Для обхода договорённости о приватности можно использовать метод "data()":

    $("#my").data("expose")._destroy() // место для смайла "(evil)"

    В данном примере, я постарался задать хороший тон написания виджетов – я "повесил" обработчики событий в namespace, это даст в дальнейшем возможность контролировать происходящее без необходимости залазить в код виджета, это "true story".

    Код описанный в методе "_destroy()" – избыточен, т.к. он и так выполняется в публичном "destroy()", приведён тут для наглядности.

    А для ленивых, чтобы не прописывать каждый раз "eventNamespace" в обработчиках событий, разработчики добавили в версии 1.9.0 два метода: "_on()" и "_off()", первый принимает два параметра:

  • DOM элемент, или селектор, или jQuery объект
  • набор обработчиков событий в виде объекта
  • Все перечисленные события будут "висеть" в пространстве "eventNamespace", т.е. результат будет предположительно одинаковым:

    this._on(this.element, {
    mouseover:function(event) {
    console.log("Hello mouse");
    },
    mouseout:function(event) {
    console.log("Bye mouse");
    }
    });

    Второй метод – "_off()" – позволяет выборочно отключать обработчики:

    this._off(this.element, "mouseout click");

    Ну каркас баркасом, пора переходить к функционалу – добавим произвольную функцию с произвольным функционалом:

    callMe:function(){
    console.log("Allo?");
    }

    К данной функции мы легко сможем обращаться как из других методов виджета так и извне:

    // изнутри
    this.callMe()
    // извне
    $("#my").expose("callMe")

    Если ваша функция принимает параметры, то передача оных осуществляется следующим способом:

    $("#my").expose("callMe", "Hello!")

    Если вы хотите достучаться в обработчике событий до метода виджета, то не забудьте про область видимости переменных, и сделайте следующий манёвр:

    _create: function() {
    var self = this; // вот он!
    this.element.on("click."+this.eventNamespace, function(){
    // тут используем self, т.к. this уже указывает на
    // элемент по которому кликаем
    self.callMe();
    })
    },

    Хорошо идём, теперь поговорим о событиях – для более гибкой разработки и внедрения виджетов предусмотрен функционал по созданию произвольных событий и их "прослушиванию":

    // инициируем событие
    this._trigger("incomingCall");
    // подписываемся на событие при инициализации виджета
    $("#my").expose({
    incommingCall: function(ev) {
    console.log("din-don");
    }
    })
    // или после, используя в качестве имени события
    // имя виджета + имя события
    $("#my").bind("exposeincomingCall", function(){
    console.log("tru-lya-lya")
    });

    Материала много, я понимаю, но ещё добавлю описание нескольких методов которые можно вызвать из самого виджета:

    _delay() – данная функция работает как "setTimeout()", вот только контекст переданной функции будет указывать на сам виджет (это чтобы не заморачиваться с областью видимости)

    _hoverable() и _focusable() – данным методам необходимо скармливать элементы для которых необходимо отслеживать события "hover" и "focus", чтобы автоматически добавит к ним классы "ui-state-hover" и "ui-state-focus" при наступлении оных

    _hide() и _show() – эти два метода появились в версии 1.9.0, они созданы дабы стандартизировать поведение виджетов при использовании методов анимации, настройки принято прятать в опциях под ключами "hide" и "show" соответственно. Использовать методы следует следующим образом:

    options: {
    hide: {
    effect: "slideDown", // настройки эквиваленты вызову
    duration: 500 // .slideDown( 500)
    }
    }
    // внутри виджета следует использовать вызовы _hide() и _show()
    this._hide( this.element, this.options.hide, function() {
    // это наша функция обратного вызова
    console.log('спрятали');
    });

    Существует ещё пару методов, которые реализованы за нас:

    enable: function() {
    return this._setOption( "disabled", false );
    },
    disable: function() {
    return this._setOption( "disabled", true );
    },

    Фактически, данный функции создают синоним для вызова:

    $("#my").expose({ "disabled": true }) // или false

    Наша задача сводится лишь к отслеживанию данного флага в методе "_setOption()".

    Примеру быть, возможно этот виджет и не будет популярен, зато он наглядно демонстрирует как создавать виджеты для jQuery UI.

    <!DOCTYPE html>
    <html dir="ltr" lang="en-US">
    <head>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Пример widget'а для jQuery UI</title>
        <link rel="profile" href="http://gmpg.org/xfn/11"/>
        <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/>
        <link rel="stylesheet" href="css/styles.css"/>
        <style>
            .expose-widget {
                position: absolute;
                z-index: 999;
                background-color: rgba(0, 0, 0, 0.42);
                display: none;
            }
        </style>
        <script type="text/javascript" src="js/jquery.js"></script>
        <script type="text/javascript" src="js/jquery-ui.js"></script>
        <script type="text/javascript" src="js/code.js"></script>
    	<script>
            (function( $, undefined ) {
                $.widget( "book.expose", {
                    version: "1.0.0",
                    // наши настройки
                    options: {
                        color: null, // цвет фона
                        speed: 500,  // скорость анимации
                        show: 500,   // настройки для метода _show()
                        hide: {      // настройки для метода _hide()
                            effect: "fade",
                            duration: 500
                        }
                    },
                    // инициализация
                    _create: function() {
                        var self = this;
                        // выбранному элементу добавляем наш класс
                        // и вешаем обработчик события "клик"
                        this.element
                            .addClass( "book-widget" )
                            .on('click.expose-widget.'+this.eventNamespace, function() {
                                // вызываем метод виджета
                                // виджет доступен в переменной self
                                if (self.element.data('expose-it')) {
                                    self.unmask();
                                } else {
                                    self.mask();
                                }
                                return false;
                            });
                        // нам необходимо инициировать окружение лишь один раз для всех элементов
                        // это наш флаг
                        var totalInited = $.data(document.body, 'expose');
                        if (!totalInited) {
                            // изменяем флаг - у нас уже запущена инициализация
                            $.data(document.body, 'expose', 1);
                            // инициализация верхнего "бокса"
                            // и добавление в DOM
                            var $topBox = $('<div></div>');
                                $topBox.addClass("expose-widget");
                                $topBox.appendTo(document.body);
                            // сохраняем на него ссылку в реестре
                            $.data(document.body, 'topBox',    $topBox);
                            // клонируем верхний бокс
                            // добавляем и сохраняем
                            var $bottomBox = $topBox.clone();
                                $bottomBox.appendTo(document.body);
                            $.data(document.body, 'bottomBox', $bottomBox);
                            // повторяем для левого
                            var $leftBox = $topBox.clone();
                                $leftBox.appendTo(document.body);
                            $.data(document.body, 'leftBox',   $leftBox);
                            // и для правого
                            var $rightBox = $topBox.clone();
                                $rightBox.appendTo(document.body);
                            $.data(document.body, 'rightBox',  $rightBox);
                            // все боксы разом
                            $allBox = $topBox.add($bottomBox).add($leftBox).add($rightBox);
                            $.data(document.body, 'allBox',  $allBox);
                            // вешаем единый обработчик события для всех боксов
                            $(document.body).on('click.expose-widget', '.expose-widget', function() {
                                // вызов метода виджета
                                self.unmask();
                                return false;
                            });
                            // кидаем событие, на него можно подписаться лишь при инициализации виджета
                            // иначе толку не будет - будет поздно
                            this._trigger('build');
                        } else {
                            // отслеживаем кол-во виджетов
                            totalInited++;
                            $.data(document.body, 'expose', totalInited);
                        }
                        // вызываем метод виджета для применения цвета
                        this._setCurrentColor();
                    },
                    // ломаем всё что строили
                    _destroy: function() {
                        // подчищаем наш элемент
                        this.element
                            .removeClass( "book-widget" )
                            // данный метод избыточен, т.к. это делает за нас публичный метод destroy()
                            .off('.'+this.eventNamespace);
                        // декрементим кол-во инициализированных виджетов
                        var totalInited = $.data(document.body, 'expose');
                        totalInited--;
                        $.data(document.body, 'expose', totalInited);
                        // если больше нет виджетов
                        // удаляем вспомогательные боксы
                        if (totalInited==0) {
                            $.data(document.body, 'allBox').remove();
                            $(document.body).off('.expose-widget');
                            this._trigger('destroy');
                        }
                    },
                    // применяем настройки
                    _setOption: function( key, value ) {
                        if (key == 'disabled') {
                            // тут должен быть код по отключению виджета
                        }
                        if (key == 'speed') {
                            this.options.show = value;
                            this.options.hide.duration = value;
                        }
                        this._super( key, value );
                    },
                    // применяем настройки цвета
                    _setCurrentColor: function() {
                        this.options.color = $.data(document.body, 'topBox').css('background-color');
                    },
                    // изменяем цвет
                    color: function(value) {
                        $.data(document.body, 'allBox').css('background-color', value);
                        this._setCurrentColor();
                    },
                    // прячем боксы
                    unmask: function() {
                        var self = this;
                        // получаем набор
                        var $allBox = $.data(document.body, 'allBox');
                        // вызываем функцию анимации
                        this._hide($allBox, this.options.hide, function() {
                            // бросаем событие по завершению
                            self._trigger('unmask')
                        });
                        this._resetFlag();
                        // изменяем флаг
                        this.element.data('expose-it', false);
                    },
                    // показываем боксы
                    mask: function() {
                        var self = this;
                        // берём все "боксы"
                        var $allBox = $.data(document.body, 'allBox');
                        var $topBox = $.data(document.body, 'topBox');
                        var $bottomBox = $.data(document.body, 'bottomBox');
                        var $leftBox = $.data(document.body, 'leftBox');
                        var $rightBox = $.data(document.body, 'rightBox');
                        // вычисляем текущие координаты элемента и размеры окна
                        var offset = this.element.offset();
                        var height = this.element.outerHeight();
                        var width  = this.element.outerWidth();
                        var winWidth = $(document).width();
                        var winHeight = $(document).height();
                        var topSettings = {
                            top:0,
                            left:0,
                            width:winWidth,
                            height:offset.top,
                            backgroundColor:this.options.color
                        };
                        var leftSettings = {
                            top:offset.top,
                            left:0,
                            width:offset.left,
                            height:height,
                            backgroundColor:this.options.color
                        };
                        var rightSettings = {
                            top:offset.top,
                            left:offset.left+width,
                            width:winWidth - (offset.left+width),
                            height:height,
                            backgroundColor:this.options.color
                        };
                        var bottomSettings = {
                            top:offset.top+height,
                            left:0,
                            width:winWidth,
                            height:winHeight - (offset.top+height),
                            backgroundColor:this.options.color
                        };
                        // проверяем - отображается у нас боксы в данный момент времени или нет
                        if ($topBox.is(':hidden')) {
                            // применяем настройки
                            $topBox.css(topSettings);
                            $leftBox.css(leftSettings);
                            $rightBox.css(rightSettings);
                            $bottomBox.css(bottomSettings);
                            // показываем все наши боксы
                            this._show( $allBox, this.options.show, function() {
                                // событие mask
                                self._trigger('mask');
                            });
                        } else {
                            this._resetFlag();
                            // применяем настройки через анимацию
                            $topBox.animate(topSettings, this.options.speed);
                            $leftBox.animate(leftSettings, this.options.speed);
                            $rightBox.animate(rightSettings, this.options.speed);
                            $bottomBox.animate(bottomSettings, this.options.speed, function(){
                                // событие mask
                                self._trigger('mask');
                            });
                        }
                        // изменяем флаг
                        this.element.data('expose-it', true);
                    },
                    _resetFlag:function() {
                        // workaround - надо "сбросить" флаг для всех других элементов
                        // надо будет придумать более красивое решение
                        $.each($.cache, function(i, el) {
                            if (el.data != undefined  el.data.exposeIt != undefined) {
                                el.data.exposeIt = false;
                            }
                        });
                    }
                });
            })( jQuery );
    	</script>
    </head>
    <body>
        <div id="content" class="wrapper box">
            <menu label="Try...">
    			<a href="tabs.html" title="go prev" class="button alignleft" rel="prev">← Prev </a>
                <a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a>
                <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a>
                <hr/>
                <pre><code><em>// run widget and try to click on article</em>
    $(<span>'article'</span>).expose()</code></pre>
                <button type="button" class="code">Run Code</button>
                <pre><code contenteditable="true"><em>// configuration was changed</em>
    $(<span>'article'</span>).expose({speed:2000})</code></pre>
                <button type="button" class="code">Run Code</button>
                <pre><code contenteditable="true"><em>// run widget and try to click on image</em>
    $(<span>'article img'</span>).expose({speed:100})</code></pre>
                <button type="button" class="code">Run Code</button>
                <pre><code><em>// try widget API - trigger</em>
    $(<span>'article:eq(0)'</span>).bind(<span>'exposemask'</span>,
        function(){
            $(this).css(<span>'color'</span>, <span>'red'</span>);
        });
    $(<span>'article:eq(0)'</span>).bind(<span>'exposeunmask'</span>,
        function(){
            $(this).css(<span>'color'</span>, <span>'black'</span>);
        })</code></pre>
                <button type="button" class="code">Run Code</button>
                <pre><code><em>// try widget API - call method</em>
    $(<span>'article:eq(1)'</span>).expose(<span>'mask'</span>)</code></pre>
                <button type="button" class="code">Run Code</button>
                <pre><code contenteditable="true"><em>// try widget API - call method+args</em>
    $(<span>'article:eq(1)'</span>).expose(
        <span>'color'</span>,
        <span>'rgba(255, 0, 0, 0.5)'</span>
    )</code></pre>
                <button type="button" class="code">Run Code</button>
            </menu>
            <header>
                <h1>widget</h1>
                <h2>Пишем своё виджет для jQuery UI</h2>
            </header>
            <article>
                <h2>Article</h2>
                <p>
                    <img src="images/photo-bumblebee-tumb.jpg" alt="Bumblebee" class="left" width="200"/>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rutrum,
                lectus eu varius consectetur, libero velit hendrerit augue, ut posuere enim neque
                in libero. Donec eget sagittis nibh. Suspendisse sed tincidunt urna. Cras quis
                euismod neque. Maecenas auctor ultricies posuere. Pellentesque luctus pulvinar dui
                eget semper. Donec sodales odio eu sapien varius luctus. Donec dictum feugiat diam
                at malesuada. Sed nec massa in augue condimentum faucibus quis ut diam. Quisque
                nisl sem, semper nec vulputate vel, mattis sit amet justo. Aliquam purus felis,
                tempor at scelerisque quis, tincidunt in neque. Etiam ut risus diam. Pellentesque
                fermentum risus id elit feugiat cursus. Ut fringilla dictum diam, sed iaculis
                lorem pulvinar ut. Cras vel elit id velit commodo viverra sit amet vel orci.</p>
            </article>
            <article>
                <h2>Article</h2>
                <p>
                    <img src="images/photo-chamomile-tumb.jpg" alt="Chamomile" class="left" width="200"/>
                Duis in vestibulum sem. Cras euismod tincidunt dui, et scelerisque tellus condimentum vel.
                Maecenas et urna sit amet risus fermentum rhoncus nec porttitor ligula. Maecenas sit amet
                turpis enim, ut iaculis est. Duis feugiat, lacus id placerat porttitor, lorem augue gravida
                nisi, eu porta eros risus et lectus. Maecenas vestibulum nunc vel ipsum tincidunt sit amet
                blandit sapien bibendum. Proin vel vulputate nisl. Duis tempor imperdiet placerat. Pellentesque
                faucibus consequat magna, et bibendum nisl egestas non. Pellentesque sit amet mattis augue.
                Aenean at diam tincidunt purus sollicitudin gravida non in nisi. Fusce bibendum, magna in
                adipiscing mattis, sem risus fringilla mi, nec gravida lectus lectus at nibh. Suspendisse
                adipiscing elementum laoreet. Suspendisse sem erat, varius quis aliquet vitae, dapibus sed
                nibh. Nullam iaculis sem at mauris faucibus in vestibulum libero pretium. Aliquam eu turpis
                libero. Fusce et ultrices lectus.</p>
            </article>
            <article>
                <h2>Article</h2>
                <p>
                    <img src="images/photo-maple-leaf-tumb.jpg" alt="Maple Leaf" class="left" width="200"/>
                Ut consequat commodo mauris, eu dignissim justo congue vel. Etiam commodo tincidunt diam,
                laoreet ullamcorper sapien egestas quis. Etiam auctor rutrum ante, at tincidunt elit lacinia
                non. Pellentesque molestie tellus sit amet est sodales nec rutrum leo pharetra. Donec lacinia
                ipsum vitae massa accumsan ullamcorper. Maecenas commodo lacus turpis. Proin sit amet mauris
                sem, imperdiet faucibus lorem. Fusce ullamcorper consectetur ligula vel pretium. Sed et elit
                vitae orci adipiscing condimentum id sed turpis. Morbi ultrices feugiat ullamcorper. Fusce at
                magna dolor. Sed sit amet risus massa, quis imperdiet libero. Proin justo purus, sodales nec
                cursus et, sollicitudin at nulla. Vivamus eget nibh tellus, sit amet facilisis ante.</p>
            </article>
            <footer>
                ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a>
            </footer>
            <script type="text/javascript">
                var _gaq = _gaq || [];
                _gaq.push(['_setAccount', 'UA-1669896-2']);
                _gaq.push(['_trackPageview']);
                (function() {
                 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
                 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
                })();
            </script>
    	</div>
    </body>
    </html>
    

    Будьте внимательны, с выходом jQuery UI версии 1.9.0 были внесены правки в Widget API, следовательно, большинство доступной инфор- мации устарело, так что читайте официальную документацию [http://wiki.jqueryui.com/w/page/12138135/Widget%20factory], а ещё лучше – заглядывайте в код готовых виджетов "от производителя"

    Информация по теме разработки виджетов:

  • "The jQuery UI Widget Factory. WAT?" – эта документации актуальна [http://ajpiano.com/widgetfactory/]
  • "Understanding jQuery UI widgets: A tutorial" [http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/]
  • "Tips for Developing jQuery UI 1.8 Widgets" [http://www.erichynds.com/jquery/tips-for-developing-jquery-ui-widgets/]
  • "Coding your First jQuery UI Plugin" [http://net.tutsplus.com/tutorials/javascript-ajax/coding-your-first-jquery-uiplugin/]
  • jQuery Tools

    Альтернатива jQuery UI, хотя я бы назвал полезным дополнением. Библиотека jQuery Tools состоит из компонентов, которые разделяются на три типа:

  • UI Tools – интерактивные компоненты интерфейса
  • Form Tools – компоненты по работе с формами
  • Toolbox – всяко-разно и полезно
  • UI Tools

    Tabs – аналогично jQuery UI по назначению, скромнее по функционалу, плюс еще есть возможность настраивать как "accordion":

    Tooltip – всплывающие подсказки, я не уверен в юзабельности оных, но может для кого-то данный компонент окажется полезным, ведь его так же включили в последний jQuery UI:

    Overlay – создание всплывающих модальных окошек, полезный компонент, аля "lightbox":

    Scrollable – компонент для создания "карусели" из картинок, может пригодиться в любом онлайн-магазине:

    Form Tools

    Validator – позволяет на декларативном уровне задавать правила проверки вводимых значений, что бывает очень полезно, и крайне удобно (совместим с HTML5).

    RangeInput – аналогичен jQuery UI Slider

    DateInput – аналогичен jQuery UI Datepicker

    Toolbox

    Expose – пожалуй самый замечательный компонент из всего набора – он позволяет выделять элемент на странице, путём затемнение остальных элементов; это трудно описать, лучше посмотрите на демо: http://jquerytools.org/demos/toolbox/expose/index.html

    Flashembed – компонент для быстрого и удобного встраивания Flash-объектов на страницу, прямой конкурент SWFObject

    Данный набор утилит достаточно долго не обновлялся, с марта 2012-го года, но он ещё не канул в лету, работа идёт, и обещают выпустить вторую версию.

    На этом, пожалуй, стоит закончить этот обзор.

    jQWidgets

    jQWidgets – это не просто библиотека, а целый комбайн с кучей полезных виджетов, да их на данный момент насчитывается тридцать четыре штуки, тут только привести список и скриншот на каждый – уже пол книги будет, так что обойдёмся кратеньким резюме на каждый виджет. Начну обзор с комплексных и "тяжёлых" виджетов, к ним картинки я таки приложу.

    jqxGrid – это датагрид с кучей полезных и не очень примочек, тут понятное дело есть постраничная навигация, сортировки, фильтрации и группировки, только по этому виджету уже можно здоровенный талмуд написать:

    jqxChart – виджет для построения разнообразных графиков с помощью HTML, CSS и JavaScript, сделано всё очень и очень культурно, особенно выделю функцию по сохранению графика как картинки, иногда очень её не хватает:

    jqxGauge – этот виджет не часто встретишь и в даже более именитых фреймворках, но по сути – это некий измеритель, т.е. с его помощью можно нарисовать спидометр, манометр, термометр или ещё какой другой измерительный прибор с произвольной шкалой:

    jqxTreeMap – ещё один редкий вид, скорее даже уникальный, с его помощью можно построить связанное дерево в виде организованных прямоугольничков, если ничего не понятно, то лучше просмотреть демку, ну и скриншот прилагаю:

    jqxTree – это уже не столь экзотический виджет, как понятно из названия, будем садить деревья:

    На этом обзор "крутых" виджетов можно заканчивать, углубляться в скучные и обыденный обёртки над элементами форм мне не хочется, замечу лишь, что во многом данный фреймворк обходит jQuery UI, но не всё так радужно в этом королевстве:

    Данный фреймворк распространяется под лицензией Creative Commons Attribution-NonCommercial 3.0 License, которая предусматри- вает бесплатное использование библиотеки для не коммерческих проектов, иначе – смотрите расценки.

    Ещё стоит упомянуть одну приятную особенность – это возможность легкой интеграции с MVVM фреймворком Knockout, но это уже другая история

    jQuery Mobile

    А вот это вполне самостоятельный продукт, и как следует из названия предназначен для создания интерфейсов для мобильных устройств с поддержкой "Touch Screen". Этот фреймворк хорошо документирован, с кучей примеров, которые можно пощупать на http://jquerymobile.com/

    Данный фреймворк хорошо подходит для создания мобильных версий сайтов, но при этом он будет выглядеть как мобильное приложение, хотя нет – сайт будет выглядеть как сайт на jQuery Mobile. Насколько это хорошо или плохо мне судить сложно, скажем так – это востребовано.

    Я не буду рассматривать все компоненты данного фреймворка, приведу лишь некоторые скриншоты (взяты с официального сайта http://jquerymobile.com/):

    Для jQMobile существует свой собственный ThemeRoller – http://jquerymobile.com/themeroller/

    И еще, посоветую обратить внимание на API данного фреймворка, без изучения оного вам будет затруднительно создать действительно интересные приложения.

    Еще плагины

    Хотелось бы порекомендовать еще несколько плагинов, которые стоит всегда держать под рукой:

    color — если потребуется анимация цвета фона, либо шрифта, либо еще чего-нибудь (если не хотите использовать jQuery UI) [https://github.com/jquery/jquery-color]

    cookie— удобная работа с "печеньками" в браузере [http://archive.plugins.jquery.com/project/Cookie]

    easing — расширяем стандартный набор функций easing (об этом я рассказывал в главе анимация) [http://gsgd.co.uk/sandbox/jquery/easing/]

    form — упрощает работу с формами, сам уже давно не пользуюсь, но для быстрого старта самое оно[http://malsup.com/jquery/form/]

    hotkeys — название говорит само за себя [https://github.com/jeresig/jquery.hotkeys]

    mouswheel — добавляет к jQuery возможность отслеживать события колесика мышки (да, да, из коробки этого функционала нет в библиотеке) [http://brandonaaron.net/code/mousewheel/docs]

    profiler – как и следует из названия – помогает отлаживать код jQuery приложений [http://archive.plugins.jquery.com/project/profile] [http://ejohn.org/blog/deep-profiling-jquery-apps/]

    shadow animation — как следует из названия – плагин для анимации теней [http://www.bitstorm.org/jquery/shadow-animation/]

    jQuery Transition Events – поддержка CSS Transition из JS [https://github.com/ai/transition-events]

    SWFObject — если потребуется вставить какую-нибудь flash’ку на страницу, данный плагин облегчит вам участь[http://jquery.thewikies.com/swfobject/]

    SWFObject — если потребуется вставить какую-нибудь flash’ку на страницу, данный плагин облегчит вам участь[http://jquery.thewikies.com/swfobject/]

    Обновление на версию 2.х

    Чем же нам грозит новая версия? Ну нам то боятся её не стоит, а вот пользователи старых версий Internet Explorer не смогут больше насладиться удобством столь популярной библиотеки. Да, да, именно так, из второй версии убрали поддержку IE с 6-ой по 8-ую. Благодаря этой "оптимизации" библиотека похудела на 10%.

    Если более современные IE работают в режиме совместимости со старыми версиями, то jQuery не будет заморачиваться, и откажется работать. Избежать это поможет мета-тег X-UA-Compatible или заголовок HTTP.

    В скором времени "под нож" пойдут и другие устаревающие браузеры, будьте бдительны Android/WebKit 2.x, за вами уже выехали.

    Если вас не зацепила эта диета, то можно пойти дальше, и собрать jQuery только из того, что вам действительно необходимо, что для этого потребуется – читайте в подробном пошаговом руководстве, которому и перевод-то не требуется.

    Если вас не впечатляет этот список измений, то это лишь благодаря труду разработчиков jQuery, которые постарались обеспечить полную совместимость API веток 1.х и 2.х, а вот это уже действительно впечатляет.

    Ну ладно, не всё так гладко с совместимостью, был этот путь тернист, и проблемы с обновлением будут, вот только искать причину нужно будет в списке изменений версии 1.9, именно эта версия стала поворотной в вопросе совместимости веток.

  • "How to build your own jQuery"[https://github.com/jquery/jquery/]
  • "jQuery Core 1.9 Upgrade Guide" [http://jquery.com/upgrade-guide/1.9/]
  • История изменений

    1.0.0 alpha Первая публикация "для своих", всего 75 скачиваний, вычитка началась
    1.0.0 beta Исправление опечаток и грамматических ошибок. Публикация на Хабре и более 13 000 скачиваний
    1.0.0 Релиз :) Исправлено множество опечаток и несколько ссылок. Добавлена информация о быстродействии селекторов вида "div#id" – спасибо хабрапользователю skorney за тестирование. Более 2 000 скачиваний
    1.0.1 Ещё несколько мелких правок. Спасибо читателям за отзывы. 4500+
    1.0.2 И ещё… Опубликовал в ePub формате, выложил на scribd, получилось более 8 000 скачиваний и более 10 000 прочтений на scribd
    1.0.3 Обновил книгу вслед за обновлением jQuery, добавил информацию о изменениях в версии 2.х относительно 1.х. Добавил описание виджетов jQWidgets. Удалил описание событий для метода data(). Примеры кода используемые в учебнике теперь доступны на GitHub, так что жду pull-запросов – https://github.com/AntonShevchuk/jquery-for-beginners. Счётчик прочтений на scribd перевалил за 20 тысяч, при этом версию 1.0.2 скачали более 10 тысяч раз…
    1.0.4 Оптимизация шрифтов под печать формата А5. Ушла в тираж.
    1.0.5 Ждёмс…
    Вернуться к учебному плану