Использование библиотеки jQuery UI на практике

105

Библиотека jQuery UI состоит из виджетов и взаимодействий, которые позволяют создавать насыщенные веб-приложения со стилевым оформлением пользовательского интерфейса, подчиняющимся определенной теме, и обеспечивают чрезвычайно гибкие возможности настройки в соответствии с конкретными задачами. Ранее мы рассмотрели все эти виджеты и взаимодействия и в этой статье некоторые из указанных средств будут добавлены в наш базовый пример для демонстрации того, каким образом можно организовать их совместную работу.

В качестве отправной точки для данной статьи мы используем вариант документа, приведенный в примере ниже:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>jQuery UI</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="//ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/jquery-ui.min.js"></script>
    <link rel="stylesheet" href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/themes/sunny/jquery-ui.css">
    
    <!-- Локальные файлы -->
    <link rel="stylesheet" href="styles.css">
    <script src="jquery.tmpl.min.js" type="text/javascript"></script>
    
    <script type="text/javascript">
        $(function() {
            $.getJSON("mydata.json", function(data) {
                $('#flowerTmpl').tmpl(data).appendTo("#products");
            });            
        });
    </script>
    <script id="flowerTmpl" type="text/x-jquery-tmpl">
        <div class="dcell">
            <img src="http://professorweb.ru/downloads/jquery/${product}.png"/>
            <label for="${product}">${name}:</label>
            <input name="${product}" value="0" />
        </div>
    </script>    
</head>
<body>
    <h1>Цветочный магазин</h1>
    <form>
        <div id="products"></div>
        <div id="buttonDiv"><button type="submit">Заказать</button></div>
    </form>
</body>
</html>

В этом документе необходимые элементы генерируются с помощью шаблона данных (для сокращения разметки документа) на основании информации о продуктах, извлекаемой из файла JSON с помощью метода getJSON(). Вся совокупность элементов, соответствующих отдельным видам продукции, собирается в единственном элементе с идентификатором products. Файл mydata.json находиться в одном каталоге с документом и содержит следующие данные:

[{"name":"Астра","product":"astor","stocklevel":"10","price":"2.99"},
 {"name":"Нарцисс","product":"daffodil","stocklevel":"12","price":"1.99"},
 {"name":"Роза","product":"rose","stocklevel":"2","price":"4.99"},
 {"name":"Пион","product":"peony","stocklevel":"0","price":"1.50"},
 {"name":"Примула","product":"primula","stocklevel":"1","price":"3.12"},
 {"name":"Подснежник","product":"snowdrop","stocklevel":"15","price":"0.99"}]

Также не забудьте добавить файл таблицы стилей styles.css:

h1 {
     min-width: 70px; border: thick double black; margin-left: auto;
    margin-right: auto; text-align: center; font-size: x-large; padding: .5em;
    color: darkgreen; margin-top: 0;
}
.dtable {display: table;}
.drow {display: table-row;}       
.dcell {display: table-cell; padding: 10px;}
.dcell > * {vertical-align: middle}
input {width: 2em; text-align: right; border: thin solid black; padding: 2px;}
label {width: 5em;  padding-left: .5em; display: inline-block;}
#buttonDiv {text-align: center;}
#oblock {display: block; margin-left: auto; margin-right: auto; min-width: 700px; }

Вид исходного документа в окне браузера представлен на рисунке:

Исходный документ для примеров

Отображение данных

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

...
$(function() {
    $.getJSON("mydata.json", function(data) {
        var flowers = $('#flowerTmpl').tmpl(data);
        var rowCount = 1;    
        for (var i = 0; i < flowers.length; i += 2) {
            $("<h2><a href=#>" + data[i].name + " и " + data[i + 1].name.toLowerCase()
              + "</a></h2>").appendTo("#products");
            $("<div id='row" + (rowCount++) + "'></div>")
                .appendTo("#products")
                .append(flowers.slice(i, i + 2))
        }
        $('#products').accordion();
    });            
});
...

Здесь в функцию, передаваемую методу getJSON(), добавлен код, предназначенный для создания виджета Accordion, включая построение необходимой структуры элементов и вызов метода accordion(). В новой реализации названия цветов извлекаются из соответствующего источника с помощью объекта данных JSON, но для генерации HTML-элементов (которые затем разбиваются на группы и помещаются в оболочки, образуемые элементами div, в соответствии с требованиями виджета Accordion) по-прежнему используется подключаемый модуль шаблонов данных.

Вид документа в окне браузера после добавления вызова метода accordion() представлен на рисунке:

Создание структуры элементов и вызов метода accordion()

Добавление корзины покупателя

Нашим следующим шагом будет добавление простейшей корзины покупателя, представляющей отобранные покупателем продукты. Соответствующие изменения, которые для этого требуется внести в образец документа, представлены в примере ниже:

Запустить пример

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>jQuery UI</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="//ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/jquery-ui.min.js"></script>
    <link rel="stylesheet" href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/themes/sunny/jquery-ui.css">
    
    <!-- Локальные файлы -->
    <link rel="stylesheet" href="styles.css">
    <script src="jquery.tmpl.min.js" type="text/javascript"></script>
    
    <style type="text/css">
        .dcell img {height: 60px}
        #basketTable {border: thin solid black; border-collapse: collapse}        
        th, td {padding: 4px; width: 50px}
        td:first-child, th:first-child {width: 150px}
        #placeholder {text-align: center}
        #productWrapper {float: left; width: 65%}
        #basket {width: 30%; text-align: left; float: left; margin-left: 10px}
        #buttonDiv {clear: both}
    </style>
    <script type="text/javascript">
$(function() {
	
    $.getJSON("mydata.json", function(data) {
        var flowers = $('#flowerTmpl').tmpl(data);
        var rowCount = 1;    
        for (var i = 0; i < flowers.length; i += 2) {
            $("<h2><a href=#>" + data[i].name + " и " + data[i + 1].name.toLowerCase()
              + "</a></h2>").appendTo("#products");
            $("<div id='row" + (rowCount++) + "'></div>")
                .appendTo("#products")
                .append(flowers.slice(i, i + 2))
        }
        $('#products').accordion();
		
		$('input').change(function(event) {
                    $('#placeholder').hide();
                    var fname = $(this).attr("name");
                    var row = $('tr[id=' + fname + ']');
                    if (row.length == 0) {
                        $('#rowTmpl').tmpl({
                            name: fname,
                            val:  $(this).val(),
                            product: $(this).siblings("label").text()
                        }).appendTo("#basketTable").find("a").click(function() {
                            removeTableRow($(this).closest("tr"));
                            var iElem = $('#products').find("input[name=" + fname + "]")
                            $('#products').accordion("option", "active",
							    Number(iElem.closest("div[id^=row]").attr('id').substring(3)) - 1)
                            iElem.val(0).select();    
                        })
                    } else if ($(this).val() != "0") {
                        row.children().eq(1).text($(this).val())
                    } else {
                        removeTableRow(row)
                    }                    
                })
    });    
	
	function removeTableRow(row) {
          row.remove();                 
          if ($('#basketTable tbody').children(':visible').length == 1) {
               $('#placeholder').show();
          } 
    }     
});
    </script>
    <script id="rowTmpl" type="text/x-jquery-tmpl">
        <tr id=${name}><td>${product}</td><td>${val}</td>
            <td><a href=#>Удалить</a></td></tr>
    </script>
    <script id="flowerTmpl" type="text/x-jquery-tmpl">
        <div class="dcell">
            <img src="http://professorweb.ru/downloads/jquery/${product}.png"/>
            <label for="${product}">${name}:</label>
            <input name="${product}" value="0" />
        </div>
    </script>    
</head>
<body>
    <h1>Цветочный магазин</h1>
    <form>
        <div id="productWrapper">
            <div id="products"></div>
        </div>
        <div id="basket" class="ui-widget">
            <table border=1 id="basketTable">
                <tr><th>Продукт</th><th>Количество</th><th>Удалить</th></tr>
                <tr id="placeholder"><td colspan=3>Ничего не выбрано</td></tr>
            </table>
        </div>
        <div id="buttonDiv"><button type="submit">Заказать</button></div>
    </form>
</body>
</html>

Давайте рассмотрим этот код более подробно.

Помещение виджета Accordion в оболочку

Мы хотим, чтобы корзина покупателя отображалась рядом с панелями виджета Accordion. Для этого мы помещаем элемент, для которого вызывается метод accordion(), внутрь другого элемента div:

<div id="productWrapper">
      <div id="products"></div>
</div>

Работа виджета Accordion будет нарушена, если окажется, что он не занимает все пространство родительского элемента по ширине, поэтому мы добавляем оболочку и фиксируем ее ширину с помощью CSS-свойства width:

#productWrapper {float: left; width: 65%}

Таким образом, виджет Accordion, как и должно быть, благополучно располагается по всей ширине элемента-оболочки div, который занимает только 65% ширины своего родительского элемента.

Добавление таблицы

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

<div id="basket" class="ui-widget">
    <table border=1 id="basketTable">
        <tr><th>Продукт</th><th>Количество</th><th>Удалить</th></tr>
        <tr id="placeholder"><td colspan=3>Ничего не выбрано</td></tr>
    </table>
</div>

Как и в случае виджета Accordion, мы помещаем элемент table в оболочку, ширину которой устанавливаем с помощью CSS-свойства:

#basket {width: 30%; text-align: left; float: left; margin-left: 10px}

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

Обработка изменений входных значений

Чтобы связать таблицу с виджетом Accordion, мы реагируем на события change, порождаемые элементами input, которые создаются в функции getJSON(). Это делается с помощью следующей функции-обработчика:

$('input').change(function(event) {
        $('#placeholder').hide();
        var fname = $(this).attr("name");
        var row = $('tr[id=' + fname + ']');
        if (row.length == 0) {
            $('#rowTmpl').tmpl({
                name: fname,
                val:  $(this).val(),
                product: $(this).siblings("label").text()
            }).appendTo("#basketTable").find("a").click(function() {
                removeTableRow($(this).closest("tr"));
                var iElem = $('#products').find("input[name=" + fname + "]")
                $('#products').accordion("activate",
                    iElem.closest("div[id^=row]").prev())
                iElem.val(0).select();    
            })
        } else if ($(this).val() != "0") {
            row.children().eq(1).text($(this).val())
        } else {
            removeTableRow(row)
        }        
})

Эта функция-обработчик решает множество задач. Прежде всего, когда пользователь изменяет какое-либо значение, осуществляется проверка того, имеется ли уже в таблице строка, соответствующая данному продукту. В случае отсутствия такой строки создается новая строка, для генерации которой используется шаблон:

<script id="rowTmpl" type="text/x-jquery-tmpl">
        <tr id=${name}><td>${product}</td><td>${val}</td>
            <td><a href=#>Удалить</a></td></tr>
</script>

Необходимые для данного шаблона значения получаются с помощью методов ядра jQuery, которые извлекают информацию из элемента input, породившего событие. Мы хотим отображать также названия продуктов, и с этой целью выполняем поиск в DOM-дереве для нахождения ближайшего элемента label и считывания его содержимого:

$(this).siblings("label").text()

Вновь созданная строка присоединяется к таблице. Ряд-заполнитель был скрыт еще раньше в самом начале выполнения функции-обработчика:

$('#placeholder').hide();

Процесс добавления новых строк таблицы представлен на рисунке. Пользователь вводит значение в текстовом поле, и как только это поле теряет фокус ввода, в корзине покупателя появляется новая позиция:

Добавление строк в таблицу корзины

Удаление строк

Вы могли заметить, что в качестве части шаблона данных в документ добавляется элемент <a>. При создании строки по шаблону мы регистрируем функцию-обработчик для этого элемента:

... .appendTo("#basketTable").find("a").click(function() {
    removeTableRow($(this).closest("tr"));
    var iElem = $('#products').find("input[name=" + fname + "]")
    $('#products').accordion("option", "active",
		Number(iElem.closest("div[id^=row]").attr('id').substring(3)) - 1)
    iElem.val(0).select();    
})
...

Первое, что мы делаем, — вызываем функцию removeTableRow(), передавая ей в качестве аргумента элемент tr, являющийся ближайшим предком элемента <a>. Для удаления указанного элемента из документа функция removeTableRow() использует метод remove(). Она также восстанавливает в таблице рад-заполнитель в случае отсутствия строк, относящихся к продуктам:

function removeTableRow(row) {
    row.remove();           
    if ($('#basketTable tbody').children(':visible').length == 1) {
         $('#placeholder').show();
    } 
}

Удалив строку из таблицы корзины покупателя, мы находим среди продуктов элемент input, связанный с данной строкой. Затем мы используем навигацию по DOM-дереву для поиска элемента, являющегося ближайшим предшествующим сестринским элементом по отношению к элементу div, который содержит данный элемент input, и передаем его индекс свойству active виджета Accordion. Это приводит к раскрытию той части виджета Accordion, которая содержит элемент, только что удаленный пользователем из корзины.

Наконец, мы устанавливаем значение данного элемента input равным 0 и вызываем метод select(), в результате чего этот элемент получает фокус ввода, а содержащееся в нем значение выделяется.

Обновление существующих строк

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

row.children().eq(1).text($(this).val())

Переменная row — это объект jQuery, содержащий элемент tr для продукта в таблице. Доступ к элементу td осуществляется по номеру позиции (с помощью метода eq()), а его содержимое устанавливается с помощью метода text().

Применение темы оформления

Теперь наша корзина функционирует вполне удовлетворительно, но ее внешний вид оставляет желать лучшего. К счастью, jQuery UI предоставляет библиотеку CSS-стилей (CSS-фреймворк), которые можно применить к элементам, чтобы они выглядели так же, как и виджеты после применения к ним выбранной вами темы стилевого оформления. В примере ниже показано, насколько просто получить требуемый результат путем добавления классов в HTML-элементы документа:

...
<div id="basket" class="ui-widget ui-widget-content">
    <table border=1 id="basketTable">
         <tr class="ui-widget-header"><th>Продукт</th><th>Количество</th><th>Удалить</th></tr>
         <tr id="placeholder"><td colspan=3>Ничего не выбрано</td></tr>
    </table>
</div>
...

Для элемента table дополнительно к использованию указанных классов задано отсутствие границ (рамки):

#basketTable {border:none; border-collapse:collapse}

Полученный результат проиллюстрирован на рисунке:

Создание кнопки jQuery UI

Наш следующий шаг — перемещение кнопки в другое место и ее преобразование в виджет jQuery UI. Для этого внесем изменения в функцию-обработчик загрузки страницы:

$(function() {
	
    ...
	
	$('#buttonDiv, #basket').wrapAll("<div />").parent().css({
         float: "left",
         marginLeft: "2px"
    })
             
    $('button').button()
});

а также добавим пару CSS-стилей:

#basket {text-align: left; /* Удалить остальные свойства */}
#buttonDiv {clear: both; margin: 5px}

Здесь мы поместили элементы buttonDiv и basket в новый элемент div и изменили некоторые CSS-стили для настройки позиций этих элементов. И наконец, мы вызываем метод button() для создания кнопки jQuery UI, как показано на рисунке:

Перемещение и преобразование элемента button

Добавление диалогового окна для завершения заказа

Мы хотим, чтобы, прежде чем завершить оформление заказа, щелкнув на кнопке "Заказать", пользователь предоставил нам некоторую дополнительную информацию о себе. Чтобы внести некоторое разнообразие, воспользуемся виджетом Dialog. Также добавим функцию обработки введенных пользователем данных. В примере ниже показан конечный код примера, а затем мы более подробно рассмотрим нововведения:

Запустить пример

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>jQuery UI</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="//ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/jquery-ui.min.js"></script>
    <link rel="stylesheet" href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/themes/sunny/jquery-ui.css">
    
    <!-- Локальные файлы -->
    <link rel="stylesheet" href="styles.css">
    <script src="jquery.tmpl.min.js" type="text/javascript"></script>
    
    <style type="text/css">
        .dcell img {height: 60px}
        #basketTable {border: thin solid black; border-collapse: collapse}        
        th, td {padding: 4px; width: 50px}
        td:first-child, th:first-child {width: 150px}
        #placeholder {text-align: center}
        #productWrapper {float: left; width: 65%}
        #buttonDiv {clear: both}
		#basketTable {border:none; border-collapse:collapse}
        #basket {text-align: left;}
        #buttonDiv {clear: both; margin: 5px}
		
		/* 2 новых стиля */
        #completeDialog input {width: 150px; margin-left: 5px; text-align: left}
        #completeDialog label {width: 60px; text-align: right}
    </style>
    <script type="text/javascript">
$(function() {
	
    $.getJSON("mydata.json", function(data) {
        var flowers = $('#flowerTmpl').tmpl(data);
        var rowCount = 1;    
        for (var i = 0; i < flowers.length; i += 2) {
            $("<h2><a href=#>" + data[i].name + " и " + data[i + 1].name.toLowerCase()
              + "</a></h2>").appendTo("#products");
            $("<div id='row" + (rowCount++) + "'></div>")
                .appendTo("#products")
                .append(flowers.slice(i, i + 2))
        }
        $('#products').accordion();
		
		// Нужно сузить выборку элементов input, чтобы они не пересекались
		// с текстовыми полями формы в диалоговом окне
		$('#products input').change(function(event) {
                    $('#placeholder').hide();
                    var fname = $(this).attr("name");
                    var row = $('tr[id=' + fname + ']');
                    if (row.length == 0) {
                        $('#rowTmpl').tmpl({
                            name: fname,
                            val:  $(this).val(),
                            product: $(this).siblings("label").text()
                        }).appendTo("#basketTable").find("a").click(function() {
                            removeTableRow($(this).closest("tr"));
                            var iElem = $('#products').find("input[name=" + fname + "]")
							var index = iElem.closest("div[id^=row]").attr('id').substring(2)
                            $('#products').accordion("option", "active",
							    Number(iElem.closest("div[id^=row]").attr('id').substring(3)) - 1)
                            iElem.val(0).select();    
                        })
                    } else if ($(this).val() != "0") {
                        row.children().eq(1).text($(this).val())
                    } else {
                        removeTableRow(row)
                    }                    
                })
    });    
	
	function removeTableRow(row) {
          row.remove();                 
          if ($('#basketTable tbody').children(':visible').length == 1) {
               $('#placeholder').show();
          } 
    }
	
	$('#buttonDiv, #basket').wrapAll("<div />").parent().css({
         float: "left",
         marginLeft: "2px"
    })
    
	// Обработчик щелчка по кнопке "Заказать"
    $('#buttonDiv button').button().click(function(e) {
        e.preventDefault();
        if ($('#placeholder:visible').length) {
            
            $('<div>Вы ничего не выбрали для заказа</div>').dialog({
                modal: true,
                buttons: [{text: "OK",
                    click: function() {$(this).dialog("close")}}]
            })
        } else {
            $('#completeDialog').dialog("open");
        }
    })
	
	// Создаем и настраиваем виджет Dialog
    $('#completeDialog').dialog({
       modal: true,
	   autoOpen: false,
       buttons: [{text: "OK", click: sendOrder},
        {text: "Отмена", click: function() {
          $("#completeDialog").dialog("close");
        }}]
    });
    
	// Обработка персональных данных в диалоговом окне
    function sendOrder() {
        var data = new Object();
        $('input').each(function(index, elem) {
            var jqElem = $(elem);
            data[jqElem.attr("name")] = jqElem.val();
        })
        console.log(JSON.stringify(data));
        $('#completeDialog').dialog("close");
        $('#products input').val("0");
        $('#products').accordion("option", "active", 0)
        $('#basketTable tr').has(':not(th)').filter(':visible').remove();
        $('#placeholder').show();
    }
	
			
});
    </script>
    <script id="rowTmpl" type="text/x-jquery-tmpl">
        <tr id=${name}><td>${product}</td><td>${val}</td>
            <td><a href=#>Удалить</a></td></tr>
    </script>
    <script id="flowerTmpl" type="text/x-jquery-tmpl">
        <div class="dcell">
            <img src="http://professorweb.ru/downloads/jquery/${product}.png"/>
            <label for="${product}">${name}:</label>
            <input name="${product}" value="0" />
        </div>
    </script>    
</head>
<body>
    <h1>Цветочный магазин</h1>
    <form>
        <div id="productWrapper">
            <div id="products"></div>
        </div>
        <div id="basket" class="ui-widget ui-widget-content">
            <table border=1 id="basketTable">
                <tr class="ui-widget-header"><th>Продукт</th><th>Количество</th><th>Удалить</th></tr>
                <tr id="placeholder"><td colspan=3>Ничего не выбрано</td></tr>
            </table>
        </div>
        <div id="buttonDiv"><button type="submit">Заказать</button></div>
    </form>
    <div id="completeDialog" title="Завершите покупку">        
            <div><label for="name">Имя: </label><input name="first" /></div>
            <div><label for="email">Email: </label><input name="email" /></div>
            <div><label for="city">Город: </label><input name="city" /></div>
    </div>
</body>
</html>

Здесь мы добавили элемент div, содержимое которого будет отображаться для пользователя в элементе body, а также некоторые CSS-стили, заменяющие стили из файла styles.css, которые импортируются в документ с помощью элемента link. Для создания диалогового окна используется следующий вызов метода dialog():

$('#completeDialog').dialog({
       modal: true,
	   autoOpen: false,
       buttons: [{text: "OK", click: sendOrder},
        {text: "Отмена", click: function() {
          $("#completeDialog").dialog("close");
        }}]
});

Здесь мы создаем модальный вариант диалогового окна, в котором есть две кнопки. После щелчка на кнопке "Отмена" диалоговое окно закрывается. Щелчок на кнопке "OK" приводит к вызову функции sendOrder():

function sendOrder() {
        var data = new Object();
        $('input').each(function(index, elem) {
            var jqElem = $(elem);
            data[jqElem.attr("name")] = jqElem.val();
        })
        console.log(JSON.stringify(data));
        $('#completeDialog').dialog("close");
        $('#products input').val("0");
        $('#products').accordion("option", "active", 0)
        $('#basketTable tr').has(':not(th)').filter(':visible').remove();
        $('#placeholder').show();
}

В этой функции мы получаем значения, содержащиеся в каждом из текстовых полей, и добавляем их в виде свойств в объект, который затем преобразуем в формат JSON и выводим на консоль.

Далее мы возвращаем документ в исходное состояние путем закрытия диалогового окна, сброса значений в текстовых полях, перехода на первую вкладку виджета Accordion и очистки корзины. Само диалоговое окно выглядит следующим образом:

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

Щелчок на кнопке "OK" приводит к генерации данных в формате JSON и восстановлению исходного состояния документа. Консольный вывод имеет следующий вид:

Консольный вывод заказа

Когда пользователь щелкает на кнопке, мы проверяем, является ли элемент placeholder видимым. Это делается с помощью селектора jQuery, предоставляющего объект, который содержит элементы лишь в том случае, если указанный элемент виден на экране.

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

$('#buttonDiv button').button().click(function(e) {
        e.preventDefault();
        if ($('#placeholder:visible').length) {
            
            $('<div>Вы ничего не выбрали для заказа</div>').dialog({
                modal: true,
                buttons: [{text: "OK",
                    click: function() {$(this).dialog("close")}}]
            })
        } else {
            $('#completeDialog').dialog("open");
        }
})

Описанный прием служит хорошим примером реализации функциональности приложения на нескольких уровнях. Вместе с тем использованный подход к проверке того, выбрал ли пользователь продукты, зависит от способа реализации корзины покупателя, и если этот способ впоследствии будет изменен, то процедуру проверки также потребуется изменить.

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

Диалоговое окно, напоминающее о необходимости выбрать продукт

В этой статье мы переработали простой документ, включив в него интерактивные возможности, предлагаемые библиотекой jQuery UI. В него были добавлены виджеты Accordion, Dialog и Button, а также использовалась функциональность классов CSS-фреймворка jQuery UI для управления внешним видом других элементов.

Пройди тесты
Лучший чат для C# программистов