The jQuery plugin is jdDialog. The principle of “transit calls”
Formulation of the problem
In the process of finalizing the existing administrative page on the "self-made" engine, it became necessary to replace the coarse standard modal dialog boxes with windows that fit into the site design. Nobody will allow to rewrite the administrative part, and there is no need for this. The main condition is quick integration into existing code.
Therefore, it was decided to perform a cosmetic operation.
So, the following requirements are formulated:
- implement on jQuery 1.9,
- call like standard windows for quick code change,
- dialog boxes nesting 2-3 levels,
- replace dialog boxes of type confirm and alert.
The first thing I turned to search on Google. Existing developments did not suit me, because I wanted to keep the call syntax to the maximum ...
if(confirm('') ) {...}or offered to add quite voluminous code fragments in the form of additional functions that describe what will happen after a particular choice in a window (for example, Dialog UI).
In the process of analyzing the problem, I identified the main problems:
- the dial peer generates a dialog box from a function,
- return after selecting the control should be on the next line after the call point,
- at the same time, the function generating the html code of the dialog box has already completed execution.
The main question is how to return to the place in the code that has already slipped?
The task has become as follows:
- Stop function execution.
- Form a dialog box.
- Wait for the user's choice (it is not known when this will happen).
- Process user call.
- Continue execution from the dial peer.
Implementing this in jQuery, at least for me, seems like a pretty daunting task.
Decision principle
As a solution, callback functions or timers were tested to intercept the moment of selection.
I got the best result for this task by slightly changing the intermediate conditions of the task and implementing the following principle:
- when the dialog box is generated, we interrupt the execution,
- after selecting in the dialog box, we start the function again,
- and at the call point of the dialog box if a choice in the current “session” has already been made,
- we pass the condition "in transit" and return the selection to the script.
Thus, the formation of each dialog box is one iteration, one link in the “transit session”. The handler is called-twice — the window itself is generated the first time, the second time it passes in transit until the next condition.
However, if there are several dialog boxes within the same calling function, i.e. they have nesting, a whole “transit chain” is being formed. In each iteration - 2 function calls. And with each new dialog box in the sequence, the number of calls to the handler function doubles. I don’t think that you will ever need to invest dozens of windows, so I regard the overhead of the client’s browser resources as minimal.
It resembles recursion, but differs in that:
- in one iteration, the function runs twice,
- with each iteration, a “copy” of the function is not performed, but a step-by-step pushing through, as if a “snowball" is rolling, until the conditions of the function are completely fulfilled.
It is convenient to save the selection results in relation to the DOM element, which initiated a dialog call in the form of data- attributes, non-standard attributes or in the form of named data using the .data () function.
Assigned to this principle the working name of “transit-dialogue” or “transit” calls.
In my example, implemented as a jQuery plugin.
The plugin code with an example of calls is posted here .
During development, I encountered the following problems:
Problem No. 1)
Because dialogs can be nested, you have to save the state of each window. To do this, enter the window identifier.
To solve this problem in the dialog box call, I entered the window id as a parameter. They must be unique within the same calling function. This is inconvenience for the developer, but generating an id automatically, for example, using a hash of input parameters is risky, because theoretically, in the transit chain there can be exactly the same calls (including with the same texts). In addition, windows are created dynamically - I have not yet found a reliable sign to create an id when generating a window.
Responses are saved for each dialog initializer button, so that we get a kind of “transit namespace”, so we can use repeating window id in each function. I use 1,2, and so on.
Problem number 2)
It is necessary to distinguish between a real click on a control element and a transit one. This is necessary in order to start the entire chain of transit calls again.
Solution:
A flag has been entered for this purpose (I have jdReclick). The parameter is assigned to the button before each recall and is deleted immediately after processing the recall. Focusing on this label, we delete all the data of the “transit session” if:
- the last window in the function was processed,
- cancel was selected in one of the windows
Problem 3)
How to distinguish the last one from the window in the calling function or not. If the window is the last, we have the right to delete all the data of the “transit session” so that when the button is pressed again, the algorithm starts again.
Obstacles:
- In the calling script, there is no way to look beyond the launch point and see if there are more dialog calls.
- If branching is used, under different conditions there may be a different number of windows.
Solution Options:
- Register dialog boxes at the beginning of the script and transfer this registry to the processing script.
- In each call, pass the label whether the window is final.
- After processing the last window with a separate call, start cleaning the “transit session”. In all cases, there are additional parameters that must be remembered and not confused, this is also some inconvenience. I combined the label and id of the window, reserving 0 as the cancel flag. If it is not known in advance whether another window will be launched in the transit chain, as it depends on the user's choice, in a condition where there will be no more windows, we simply prescribe the forced cleaning of the “transit session”.
Now, in detail about the implementation in my example
The event on the element starts the initiating function of the “transit-dialogue” chain:
$('#test').click(function() { ...The actual launch of the dialog box looks like this:
$(this).jdDialogs('confirm',1,['Текст?','Заголовок'],fncname)To bind data to an element, it is necessary to pass the this selector to the plugin,
in the attributes we pass:
1 - window type (name of the plugin method),
2 - window id
3 - text parameters of the window
4 - callback function
Processing of the results can be implemented in several ways:
if(! $(this).jdDialogs('confirm',1,['Текст?','Заголовок']) ) return;
if( $(this).jdDialogs('confirm',1,['Текст?','Заголовок']) ) {
...
}
switch( $(this).jdDialogs('confirm',1,['Текст?','Заголовок']) ) {
case 1: ...;
default: return;
}
If after calling Alert there is running code, you have to use return, if not, return can be omitted.
$(this).jdDialogs('alert',0,['Сделано!','Project'])
if(! $(this).jdDialogs('alert',0,['Сделано!',project]) ) return;
alert('Код выполнен');
The plugin provides standard methods confirm, alert, their short aliases cnf, al to reduce the record. You can add your own calls.
All calls run the generic jdDialog method, in which:
- client click or repeated “transit” call is recognized
- for “transit” calls, the saved selection value is returned
- if the window is launched for the first time, the generation of the jdGetWin window itself is started
- control id is generated if not specified - jdCheckId method
In this method, you can change / add new case conditions to form your own set of buttons, and also return a separate template different from the rest in return.
Clicking on buttons handles attached events. For alert, 2 options for the closing button are offered - jdClose0 with cancellation and jdClose1 - with confirmation. What to set is configured in jdGetWin in the switch case.
The event is forwarded to the jdSetAnswer method. The method recognizes the id of the current window and the id of the control initiating the launch of the dialog box. Knowing the id of the button, we can save the selection result with the key by the window id into the "transit session".
$(id).data(fname,value);Next, destroy the window with .detach () with an animation effect such as fadeIn 10
$('.jdModalBg').detach().fadeIn(10,function() { In the callback function, we check: if canceling, we reset the “transit session”. In this method, if the function name was passed in the 4th parameter when the dialog box was called, the function is called.
if(!!fncdo) window[fncdo]();Then the transit call starts. We pass the ID of the control element - the initiator to re-click on it. Those. emulated click on the control element - the initiator of the dialogue.
methods.jdReclick(id);In my example, it’s quite simple to add arbitrary constructions with calling and processing windows.
Three-Button Window Implementation Example
1. In the call to data, add 2 more parameters: labels on two buttons instead of “OK”.
$(this).jdDialogs('confirm2bttn',0,['Мы на перепутье','Действие шаг 3','Идти налево','Идти направо'])
Using an array with texts allows you to flexibly control the number of parameters - here you just need to add two more parameters to the array.
2. Connect the call:
confirm2bttn : function(fid,data,fname) {
return methods.jdDialog('Confirm2bttn',fid,data,$(this),fname);
}
3. We connect call processing. The template itself is left old, we change only the buttons:
case 'Confirm2bttn':
var bttntext1 = data[2];
var bttntext2 = data[3];
jdBttns = ''+
''+
'';
clClass = 'jdClose0';
break;4. Add the event to the Ok2 button to distinguish between the button presses - a transit call when clicking on .jdOk2 will now return the value 2:
.on('click','.jdOk2', function() {
methods.jdSetAnswer(2,$(this));
})5. We return to the initiator script and prescribe the conditions for the different buttons:
switch($(this).jdDialogs('confirm2bttn',0,['Мы на перепутье','Действие шаг 3','Идти налево','Идти направо'])) {
case 0: return;
case 1:
alert('Идём налево');
break;
case 2:
alert('Идём направо');
break;
default:6. Well, you can assign a new style to the elements of the new window, for example, make it green with yellow text. Something like this:
.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;
}I suppose that using the principle of “transit calls” provides a way to solve problems associated with waiting for actions from the client. In this case, it is enough to use the jQuery library with the proposed extension. The presented fully functional plugin was developed for use with the jQuery library version 1.9; it also works with the latest version 3.2.1 at the time of writing.