Formulation of the problem
In the process of refining the existing administrative page on the “samopisny” engine, it became necessary to replace the rough standard modal dialog boxes with windows that fit into the site design. No one will allow the rewriting of the administrative part, and there is no need for that. The main condition is quick integration into the actual code.
Therefore, it was decided to perform cosmetic surgery.
So, the following requirements are formulated:
')
- implement on jQuery 1.9,
- call like standard windows for quick code replacement,
- nesting of dialog boxes of 2-3 levels,
- replace the dialog boxes of the types confirm and alert.
The first thing turned to search on Google. The existing designs did not suit me, because I wanted to keep the call syntax to the maximum ...
if(confirm('') ) {...}
or offered to add sufficiently large code fragments in the form of additional functions that describe what will happen after a particular choice in the window (for example, Dialog UI).
In the process of parsing the problem revealed the main problems:
- the dial peer generates a dialog box from the function,
- return after selecting the control should be carried out on the next line after the call point
- while the function that generates 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 became as follows:
- Stop the execution of the function.
- Generate a dialog box.
- Wait for the user's choice (it is not known when this will happen).
- Handle user call.
- Continue execution from point of call.
Implementing it on jQuery, at least for me, looks like a rather difficult task.
Principle of solution
As a solution, callback functions or timers were used to intercept the selection moment.
I obtained the best result for this task by slightly changing the intermediate conditions of the problem and implementing the following principle:
- when generating a dialog box, the execution is interrupted,
- after selecting in the dialog box, run the function again,
- and at the point of the dialog box call, if the selection in the current “session” has already been made,
- pass the condition of "transit" and return the selection to the script.
Thus, the formation of each dialog box is one iteration, one link of the “transit session”. The handler is called twice — the window itself is generated the first time, and the second time it transits to 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 sequence, the number of calls to the handler function doubles. I don’t think that it will ever be necessary to invest dozens of windows, so the overhead costs of client browser resources are 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 takes place, as if a snowball rolls in until the conditions of the function are completely fulfilled.
It is convenient to save the selection results in binding to the DOM element that initiated the dialog call in the form of data attributes, non-standard attributes or in the form of named data using the .data () function.
This principle has assigned the working title 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 .
As I developed, I encountered the following problems:
Problem number 1)Since dialog boxes can be nested, you have to save the state of each window. To do this, enter the ID of the window.
To solve this space in the call dialog box as a parameter entered the id window. They must be unique within the same calling function. For the developer, this is an inconvenience, but it is risky to generate the id automatically using for example a hash of the input parameters, since theoretically, in the transit chain there can be absolutely identical calls (including with the same texts). In addition, windows are created dynamically - I have not yet found a reliable sign for creating id when generating a window.
Answers are saved for each dialog initializer button, so that we get a kind of “transit namespace”, so that we can use repeated window id in each function. I use 1,2, and so on.
Problem number 2)It is necessary to distinguish the real click on the control from the transit one. This is necessary in order to start the whole chain of transit calls again.
Decision:
For this purpose, a flag is entered (I have jdReclick). The parameter is assigned to the button before each repeated call and is deleted immediately after processing the repeated call. Focusing on this label, we delete all the data of the “transit session” if:
- The last window in the function was processed,
- in one of the windows was selected cancel
Problem number 3)How to distinguish the last this window in the calling function or not. If the window is the last one, we have the right to delete all the data of the “transit session” so that when you press the button again, the algorithm is restarted.
Obstacles:
- In the calling script, it is not possible to look behind the launch point and see if there are any more interactive calls.
- If branching is used, in 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 if the window is final.
- After the last window has been processed by a separate call, start the “transit session” clearing. In all cases, there are additional parameters that need to be remembered and not to be confused, this is also some inconvenience. I combined the label and the window id, reserving 0 as the cancel flag. If it is not known in advance whether another window will be launched in the transit chain, since it depends on the user's choice, in the condition where there will be no more windows, just prescribe forced cleaning of the “transit session”.
Now in detail about the implementation in my example
The event on the element starts the initiator function of the “transit-dialog” 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, you need to pass to the plugin this selector,
in attributes we pass:
1 - window type (plugin method name),
2 - window id
3 - text window parameters
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 an executing code, you will 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 the standard methods confirm, alert, their short aliases cnf, al to shorten 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 starts
- 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, as well as output a separate template other than the rest.
Clicking the buttons handles the attached events. For alert, 2 options are offered for the closing button - jdClose0 with cancellation and jdClose1 - with confirmation. Which set is configured in jdGetWin in switch case.
The event is redirected to the jdSetAnswer method. The method recognizes the id of the current window and the id of the initiator of the launch of the dialog box. Knowing the id of the button, we can save the result of the selection with the key on the window id to the “transit session”.
$(id).data(fname,value);
Next, we destroy the window using .detach () with an animation effect, for example fadeIn 10
$('.jdModalBg').detach().fadeIn(10,function() {
In the callback function, we check: if cancellation, reset the “transit session”. In this method, if the name of the function was passed by the 4th parameter when the dialog box was called, the function is called.
if(!!fncdo) window[fncdo]();
Then the transit call starts. Pass the ID of the control - initiator to re-click on it. Those. click on the control - the initiator of the dialogue is emulated.
methods.jdReclick(id);
In my example, it is quite simple to add arbitrary constructions with calling and processing windows.
An example of the implementation of the three-button window
1. In the data call, add 2 more parameters: the inscriptions on the two buttons instead of "OK".
$(this).jdDialogs('confirm2bttn',0,[' ',' 3',' ',' '])
Using an array with texts allows you to flexibly manage the number of parameters - here you just need to add two more parameters to the array.
2. We 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 leaving the old one, changing only the buttons:
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. Add an event to the Ok2 button to distinguish between pressing the buttons - a transit call when you click on .jdOk2 will now return the value 2:
.on('click','.jdOk2', function() { methods.jdSetAnswer(2,$(this)); })
5. Return to the initiator script and set the conditions for 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 assume that the use of the principle of "transit calls" provides a way to solve problems associated with waiting for action from the client. 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 most recent version 3.2.1 at the time of this writing.