- PVSM.RU - https://www.pvsm.ru -
В процессе доработки существующей административной страницы на «самописном» движке возникла необходимость замены грубых стандартных модальных диалоговых окон на окна вписывающиеся в дизайн сайта. Переписывать административную часть никто не позволит, да и нет в этом никакой необходимости. Основное условие — быстрая интеграция в действующий код.
Поэтому принято решение выполнить косметическую операцию.
Итак, сформулированы следующие требования:
Первым делом обратился к поиску в Google. Имеющиеся разработки мне не подошли, т.к. хотелось по максимум сохранить синтаксис вызова…
if(confirm('') ) {...}
или предлагали дописывать достаточно объёмные фрагменты кода в виде дополнительные функций, описывающих что именно будет происходить после того или иного выбора в окне (например Dialog UI).
В процессе разбора задачи выявил основные проблемы:
Главный вопрос — как вернуться на то место в коде, которое уже проскочил?
Задача стала выглядит следующим образом:
Реализовать такое на jQuery, по крайней мере для меня, выглядит довольно затруднительной задачей.
В качестве решения были опробованы функции обратного вызова или таймеры для перехвата момента выбора.
Наилучший результат по данной задаче я получил немного изменив промежуточные условия задачи и реализовав следующий принцип:
Таким образом формирование каждого диалогового окна — это одна итерация, одно звено «транзитной сессии». Обработчик вызывается-дважды — первый раз генерируется само окно, второй раз проходит транзитом до следующего условия.
Однако если диалоговых окон в пределах одной вызывающей функции несколько, т.е. они имеют вложенность, формируется целая «транзитная цепочка». В каждой итерации — по 2 вызова функции. И с каждым новым диалоговом окне в последовательности количество вызовов функции-обработчика удваивается. Не думаю, что когда-либо потребуется вкладывать десятки окон, поэтому накладные расходы ресурсов браузера клиента расцениваю как минимальные.
Напоминает рекурсию, но отличается тем, что:
Результаты выбора удобно сохранять в привязке к элементу DOM, инициировавшему вызов диалога в виде атрибутов data-, нестандартных атрибутов или в виде именованных данных с помощью функции .data().
Данному принципу присвоил рабочее название «транзитно-диалоговых» или «транзитных» вызовов.
В моём примере реализовано в виде плагина jQuery.
Код плагина с примером вызовов выложен здесь [1].
По мере разработки столкнулся со следующими проблемами:
Проблема №1)
Т.к. диалоговые окна могут быть вложенными, придётся сохранять состояние каждого окна. Для этого необходимо ввести идентификатор окна.
Для решения данной пробелмы в вызове диалогового окна в качестве параметра ввёл id окна. Они должны быть уникальные в пределах одной вызывающей функции. Для разработчика это неудобство, но генерировать id автоматически, используя например хэш входных параметров рискованно, т.к. теоретически в транзитной цепочке могут быть абсолютно одинаковые вызовы (в том числе с одинаковыми текстами). Кроме того окна создаются динамически — для создания id при генерации окна надёжный признак пока не нашёл.
Ответы сохраняются для каждой кнопки-инициализатора диалога, так что мы получаем некое «транзитное пространство имён», благодаря чему можем в каждой функции использовать повторяющиеся id окон. Я использую 1,2, и так далее.
Проблема №2)
Необходимо отличать реальный клик по элементу управления от транзитного. Это нужно с целью запускать всю цепочку транзитных вызовов заново.
Решение:
Для этой цели введён флаг (у меня jdReclick). Параметр присваивается кнопке перед каждым повторным вызовом и удаляется сразу же после обработки повторного вызова. Ориентируясь на данную метку, удаляем все-данные «транзитной сессии» если:
Проблема №3)
Как отличить последнее это окно в вызывающей функции или нет. Если окно последнее, мы имеем право удалить все данные «транзитной сессии» чтобы при повторном нажатии на кнопку алгоритм запускался заново.
Препятствия:
Варианты решения:
Событие на элементе запускает функцию-инициатор «транзитно-диалоговой» цепочки:
$('#test').click(function() { ...
Собственно запуск диалогового окна выглядит так:
$(this).jdDialogs('confirm',1,['Текст?','Заголовок'],fncname)
Для привязки данных к элементу, необходимо передать в плагин селектор this,
в атрибутах передаём:
1 — тип окна (имя метода плагина),
2 — id окна
3 — текстовые параметры окна
4 — функция обратного вызова
Обработка результатов можно реализовать несколькими способами:
if(! $(this).jdDialogs('confirm',1,['Текст?','Заголовок']) ) return;
if( $(this).jdDialogs('confirm',1,['Текст?','Заголовок']) ) {
...
}
switch( $(this).jdDialogs('confirm',1,['Текст?','Заголовок']) ) {
case 1: ...;
default: return;
}
Если после вызова Alert есть выполняющийся кода, придётся использовать return, если нет — return можно опустить.
$(this).jdDialogs('alert',0,['Сделано!','Project'])
if(! $(this).jdDialogs('alert',0,['Сделано!',project]) ) return;
alert('Код выполнен');
В плагине предусмотрены стандартные методы confirm, alert, их краткие алиасы cnf, al для сокращения записи. Можно дописать собственные вызовы.
Все вызовы запускают универсальный метод jdDialog, в котором:
В данном методе можно изменить/дописать новые условия case для формирования своего набора кнопок, а также в return вывести отдельный отличный от остальных шаблон.
Клик на кнопки обрабатывают привязанные события. Для alert предложено 2 варианта закрывающей кнопки — jdClose0 с отменой и jdClose1 — с подтверждением. Какую выставить настраивается в jdGetWin в switch case.
Событие переадресовывается на метод jdSetAnswer. В методе распознаётся id текущего окна и id элемента управления-инициатора запуска диалогового окна. Зная id кнопки, можем сохранить результат выбора с ключом по id окна в «транзитную сессию».
$(id).data(fname,value);
Далее уничтожаем окно с помощью .detach() с анимационным эффектом например fadeIn 10
$('.jdModalBg').detach().fadeIn(10,function() {
В функции обратного вызова проверяем: если отмена — сбрасываем «транзитную сессию». В этом методе если при вызове диалогового окна 4-м параметром была передано имя функции, функция вызывается.
if(!!fncdo) window[fncdo]();
Затем запускается транзитный вызов. Передаём ID элемента управления — инициатора для повторного клика по нему. Т.е. эмулируется клик по элементу управлению — инициатору диалога.
methods.jdReclick(id);
В моём примере довольно просто дописать произвольные конструкции с вызовом и обработкой окон.
1. В вызове в data добавляем ещё 2 параметра: надписи на двух кнопках вместо «Ок».
$(this).jdDialogs('confirm2bttn',0,['Мы на перепутье','Действие шаг 3','Идти налево','Идти направо'])
Использование массива с текстами позволяет гибко управлять количеством параметров — здесь нужно просто дописать ещё два параметра в массив.
2. Подключаем вызов:
confirm2bttn : function(fid,data,fname) {
return methods.jdDialog('Confirm2bttn',fid,data,$(this),fname);
}
3. Подключаем обработку вызова. Сам шаблон оставляем старый, меняем только кнопки:
case 'Confirm2bttn':
var bttntext1 = data[2];
var bttntext2 = data[3];
jdBttns = '<button class="jdOk jdOk1">'+bttntext1+'</button>'+
'<button class="jdOk jdOk2">'+bttntext2+'</button>'+
'<button class="jdCancel">Отмена</button>';
clClass = 'jdClose0';
break;
4. Добавляем событие на кнопку Ok2 чтобы различать нажатие кнопок — транзитный вызов при нажатии на .jdOk2 теперь будет возвращать значение 2:
.on('click','.jdOk2', function() {
methods.jdSetAnswer(2,$(this));
})
5. Возвращаемся в скрипт-инициатор и прописываем условия для разных кнопок:
switch($(this).jdDialogs('confirm2bttn',0,['Мы на перепутье','Действие шаг 3','Идти налево','Идти направо'])) {
case 0: return;
case 1:
alert('Идём налево');
break;
case 2:
alert('Идём направо');
break;
default:
6. Ну и можно присвоить элементам нового окна новый стиль, например сделать зелёным с жёлтым текстом. Как-то так:
.jdDialogConfirm2bttn {
min-width:380px;
max-width:450px;
}
.jdDialogConfirm2bttn .jdText {
min-height:60px;
}
.jdDialogConfirm2bttn .jdHeader{
background-color: hsl(115,63%,15%);
color:#F0C800;
}
.jdDialogConfirm2bttn .jdHeader .jdClose{
background-color: hsl(114,58%,22%);
color:#F5DA50;
}
Предполагаю, что использование принципа «транзитных вызовов» предоставляет способ решения проблем, связанных с ожиданием действий от клиента. При этом достаточно использовать библиотеку jQuery с предлагающимся расширением. Представленный полностью функциональный плагин разрабатывался для использования с библиотекой jQuery версии 1.9, работает также с наиболее свежей на момент написания статьи версией 3.2.1.
Автор: drtropin
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/258940
Ссылки в тексте:
[1] здесь: https://github.com/drtorpin/jdDialog
[2] Источник: https://habrahabr.ru/post/331770/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.