Всем привет. Представляю вашему вниманию свой первый jQuery плагин (
Демо) и хочу получить конструктивную критику и советы от гуру.
Суть заключается в следующем: пользователь выбирает из выпадающего списка услугу (в нашем случае взял услуги салонов красоты), которая может являться родительской для других услуг. При клике на услугу в случае если она всё-таки является родительской для списка - аяксом подгружается этот список услуг. И так до тех пор, пока скрипт не возвратит пустой массив.
(function($) {
function isEmpty(obj) {
for (var key in obj) {
return false;
// если цикл хоть раз сработал, то объект не пустой => false
}
// дошли до этой строки - значит цикл не нашёл ни одного свойства => true
return true;
}
var defaults = {
'url' : '/',
'title' : '{ title : заголовок }',
'name' : '{ name : заголовок }'
};
var methods = {
init : function(params) {
var $this = $(this);
// актуальные настройки, будут индивидуальными при каждом запуске
var options = $.extend({}, defaults, params);
// инициализируем один раз
var init = $(this).data('dropdown');
if (init) {
return this;
} else {
$(this).data('dropdown', true);
//Украсили всё
$(this).addClass('dropdown');
$("<a/>", {
'text' : options.title,
'name' : options.name,
'class' : 'das'
}).appendTo($(this));
//Добавили UL и заполнили нулевым уровнем
$("<ul/>").appendTo($(this));
$this.dropdown('getData', 0, options.url);
//Показать/скрыть выпадающий список
$(this).on("click", "a", function() {
//Показали список
$this.dropdown('toggle');
});
//Клик по пункту меню
$(this).on('click', 'li', function() {
$this.dropdown('getData', $(this).data('id'), options.url);
if ($(this).data('id') == 0) {
var text = options.title;
} else {
var text = $(this).text();
}
$this.find("a").text(text).attr('data-id', $(this).data('id'));
});
return this;
}
},
toggle : function() {
$(this).find("ul").toggle();
},
hide : function() {
$(this).find("ul").hide();
},
getData : function(id, url) {
var $this = $(this);
$.ajax({
type : 'POST',
dataType : 'JSON',
url : url,
data : {
id : id
}
}).done(function(data) {
if (isEmpty(data)) {
return $this.dropdown('hide');
}
$this.find("ul").html('');
if (id != 0)
$this.find("ul").append("<li data-id='0' class='red'>← назад</li>");
$.each(data, function(key, val) {
$("<li/>", {
'data-id' : key,
text : val
}).appendTo($this.find("ul"))
});
return this;
}).fail(function(jqXHR, textStatus) {
$.error("Ошибка загрузки: " + textStatus);
});
}
};
$.fn.dropdown = function(method) {
if (this.length > 1) {
$.error('Плагин можно применить только к одному элементу!');
return this;
}
if (methods[method]) {
// если запрашиваемый метод существует, мы его вызываем
// все параметры, кроме имени метода прийдут в метод
// this так же перекочует в метод
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if ( typeof method === 'object' || !method) {
// если первым параметром идет объект, либо совсем пусто
// выполняем метод init
return methods.init.apply(this, arguments);
} else {
// если ничего не получилось
$.error('Метод "' + method + '" не найден в плагине jQuery.dropdown');
}
};
})(jQuery);
Вызов плагина:
$("#service").dropdown({
'url' : '/php/service.php', //JSON массив для текущего уровня
'title' : 'выбрать услугу',
'name' : 'service'
});
Логика работы PHP-скрипта который подтягивает из базы и возвращает JSON для текущего уровня:
if (isset($_POST['id']))
{
$parent_id = $_POST['id'];
}
else
{
$parent_id = 0;
}
$query = "SELECT * FROM services WHERE parent_id = $parent_id";
$res = mysql_query($query) or die(mysql_error());
$num = mysql_num_rows($res);
while ($row = mysql_fetch_array($res))
{
$result[$row['id']] = $row['name'];
}
if($result == NULL)
$result = array();
echo json_encode($result);
mysql_close();
Демо
Также остался ряд вопросов по реализации:
1. сейчас скрытие выпадающего меню происходит именно по клику на ссылку, а логичнее было бы сделать чтобы скрывалось при клике на любую часть страницы кроме .dropdown. Пробовал делать так:
$(document).on('click', function(){
$this.dropdown('hide');
});
но он срабатывает первым независимо от того, в какой части вставить этот кусок и попросту не открывается выпадающее окно.
2. при выборе последнего уровня PHP-скрипт возвращает [] и мы скрываем наш блок. По хорошему нужно в наш <ul/> после скрытия подгрузить список с parent_id == 0, то есть выполнить
$this.dropdown('getData', 0, options.url);
Но
options.url лежит в другой области видимости и недоступен в методе
getData. По идее можно передать его как параметр в этот метод, но код будет дублироваться. Можно ли как то options после инициализации сделать глобальным в пределах нашего плагина и иметь доступ к нему из всех методов плагина?