The most recent work is to do a multi-step, step-by-Step form page, this multi-step step-by-step means that the business of this page is done in multiple steps, and each step may be handled in several small steps, between large steps and small steps as a sequence of business relationships. At first thought this function was good to do, as the tab page to achieve the principle of almost, really do come down to find that the relevant logic is still quite a lot of (there may be I did not think of a better way ~), especially when this function with the form, but also the state of business data combined. I have this function related to some of the logic of abstraction into a component stepjump, this component can achieve a pure static step-by-step switching and jump, as well as the combination of business and complex logic, there is a certain versatility and flexibility, this article mainly introduces its functional requirements and implementation of ideas.
Implementation effect:
There are two effects pages: Demo.html and regist.html, the related JS are demo.js and regist.js, components are encapsulated in the stepjump.js inside, using SEAJS to do the modular. Demo.html Demo is a pure static multi-step step-by-step content switching, regist.html is a complete with the business combination of effect, is I from the recent work out, but the business data in the state is a constant (step_status) to simulate.
1. Demand Analysis
The previous rendering is incomplete, but the design is too large to be easily posted. In order to describe the functional requirements of this page, I can only spend as much effort on the text as possible and make every detail clear:
1 This page a total of four big steps, which 1,3,4 only corresponding to a small step, and 2 corresponding to three small steps, also said 1,3,4 is 1 steps can be completed, and 2 need 3 steps to complete;
2 These steps are sequential relationships that must be preceded by a 1th big step in order to take the 2nd stride; The 1th small step must be completed before the 2nd step;
3 Each big step of the first small step may have a button to return to the previous step;
4 Each big step in the middle of the small step may have 2 buttons, one to return to the last small step, one jump to the next small step;
5 Each big step of the last small step may have a button to jump to the next step;
6 If a large step contains only one small step, then it is both the first and the last step;
7 Each big step of each small step to display the content is not the same, each time only show a small step;
8 The big steps that have been completed, the big steps in progress, and the big steps to be performed later, should have different UI effects (but from the implementation effect, what has been done is an effect that is being performed)
9 The following large steps to be performed must be clicked to jump through the button in the last small step of the previous big step; The big steps that have been completed and are being performed can be jumped by clicking the step name;
10 Click on the big step name, jump to the first small step of the big steps.
The above section is the static function of the page analysis, the following to analyze the actual business needs of the page:
1 This page is open to the user to login, used for a platform to do the user occupancy application, only the completion of this check-in process, can formally enter the platform to use other functions;
2 The main business data are related to users, according to the check-in process, the user's status can be divided into:
A. Information to be filled out, if this is the status value every time you enter this page, show the "1 check-in notice" This big step, indicating that the step is underway;
B. Information to be submitted, if each entry to this page is the state value, then show the "2 Company information Submission" This big step, the small step by default to show its first;
C. Audit failed, if each entry to this page is the state value, then show "3 Waiting for audit" this big step;
D. The audit has passed, if each entry to this page is the state value, then show the "3 Waiting for audit" this big step;
E. Pending confirmation of the contract, if this is the status value every time you enter this page, then show the "4 contract signed" this big step;
3 attention is "3 waiting for Audit" and "4 contract signed" each contains 3 and 2 content, their respective content is mutually exclusive display of the relationship, but they are not a step-by-step relationship, specific to show which is entirely determined by the business state. For example, "3 Waiting for review" has the following 3 possible effects:
When from "2 Company information submit" Jump to "3 wait for audit" show the first effect;
If the page is entered when the audit has passed the status of display 2nd effect;
If you enter the page by auditing the failed status display 3rd effect, and in this case, the step name has special effects requirements:
When you click the step name, for example, click on "2 Company Information Submission", this effect has to revert to the default effect, and then click "3 Waiting for audit", but also set to this special effect, only through the "2 Company Information Submission" Step to "3 Waiting for review", can completely undo this special effect.
The general demand is these parts, there may be some details are not described, because the text is not easy to say clearly, so can only be based on the actual effect of the experience. From the final implementation, in front of the requirements, the static functional requirements is the core of the component implementation, the following business requirements are not universal, I developed this component of the starting point is based on static functional requirements to write out the basic functions of components, and then combined with business requirements to design a reasonable API and callback, And as much as possible to separate JS and HTML, components and business separation, so that the final implementation can maximize flexibility.
2. Realize the idea
First of all, the HTML structure: I think in front of the requirements, there are 2 important concepts: Big steps, small steps, and these big steps have to do with small steps, and there are sequential conventions between steps, so you have to design two sets that hold all the big step dependencies and all the small step dependencies, The HTML structure can be designed as follows:
-Hide code <nav class= "Nav-step" > <ul id= "Steps" class= "Steps" > <li>< ; a href= "javascript:;" >1<span class= "gap" ></span> check-in notice </a></li> <li><a href= "javascript:;" >2<span class= "GAP" ></span> Company Information submission </a></li> <li><a href= "javascript:;" >3<span class= "gap" ></span> awaiting audit </a></li> <li><a href= "javascript:;" >4<span class= "GAP" ></span> contract signed </a></li> </ul> </nav> <div id= " Step-content "class=" step-content "> <div class=" Step-pane "> </div> <div class=" Step-pane "> </ div> <div class= "Step-pane" > </div> <div class= "Step-pane" > </div> <div class= "Step-pane "> </div> <div class=" Step-pane "</div> </div>
Where #steps Li is all the big step items, all the #step-content. Step-pane is all the small steps. These two sets only solve the problem of storing and sequencing the step items, and the inclusion relationship between them has not been resolved. Among the requirements, big steps and small steps are the inclusion relationships:
In this way, we can simply use a simple configuration array to reflect this relationship, such as the above structure can be described with [1,3,1,1], indicating a total of 4 big steps, where 1,3,4 have only one small step, 2 have 3 small steps. Because the big steps and the small steps are stored separately in two sets, so when we access these two sets, we use the index position of the relative set, but in the actual use of the process: the position of the big step is better to identify, the absolute position of small steps is not good to identify, And the location of the relative set is starting at 0, and if there are some other components defined in each small step, such as form-related components, we will certainly put an instance of these components in a configuration table:
var step_config = {
0: {
form: {
//...
}
},
1: {
form: {
//...
}}
,
2 : {
form: {
//...}}
,
3: {
form: {
//...
}
} //...
}
This configuration table is identified by the absolute index of the small step, which is inconvenient when it is actually used, and when the HTML structure of the step is adjusted, the structure is changed, and all references to it to get the relevant components are changed. The best way to do this is to complete the following configuration:
var step_panes_config = {//2,1 represents the first step of the second step//2,2 represents the second step of the second steps
//2,3 represents the third small step of the second steps content
2: {
1: {
//Configure small step related things
},
2: {/
/configure small step related things
},
3: {
//Configure small step related things
}
Configure big step related things
}
}
It is the equivalent of abstracting the previous inclusion structure into:
This structure has two advantages: first, regardless of the set index from the beginning of the 0 problem, Step_panes_config[2] represents the 2nd big step; the second is that the index of the small step does not consider the problem from the beginning of 0, and it is a relatively large step to identify, such as step_panes_config[ 2][1] Represents the first step of the 2nd big step, so that large steps and small steps can be well identified by the index, and the configuration table is more stable. That is, when the component provides an index-related interface or parameter externally, it is provided in a conventional way, in which the logical index (such as [2][1]) is transformed from the physical index, and the transformation relationship between the physical index and the logical index is resolved within the component. For example, when the external call, tell the component initialization needs to show the 2nd big step of the 1th small step, then the component must be based on this information to find the appropriate size steps to display; When the physical index of a step item is externally known, the component provides a way to convert the physical index location to a logical index
Again, the effect:
1 each step of the content as long as the control to show which can be, so the step content if using CSS to control the state of the words are only 2 kinds, the default state and active state, the default state does not show, active state display;
2 The corners of each step can be realized by the principle of the triangle with the CSS border;
3 in order to correctly control the effect of the step, each step if the state with the CSS control of the word there are 3, default state, done state and current state, respectively, that is not executed, executed and are executing the steps. Another big step is a alerts state, but it's a business-related condition that has nothing to do with components. This three-state control implementation is similar to the online scoring component.
This is probably the idea, plus the design of APIs and callbacks, which I'll describe in the next part of the implementation details.
3. Implementation Details
Let's take a look at the configuration items for the component:
var DEFAULTS = {
config: [],//Required parameters, the configuration of the step item and the step content item, as [1,2,3] represents a total of three (config.length) steps, and the 1th step has 1 (config[0]) content items. The 2nd step has 2 (config[1]) content items, and the 3rd step has 3 (config[2) content items
steppanes: ',//required parameters, the JQ selector for the step content item
navsteps: ',//must pass the parameter, The JQ selector for the step item
initstepindex:1,//The position of the step initially displayed, if there are a total of 4 steps, the optional value of the parameter is: 1,2,3,4
initpaneindex:1,//At the beginning of the step content item location, Based on Initstepindex, if the Initstepindex is set to 2 and the step has 3 content items, the optional value for this parameter is: 1,2,3
onstepjump: $.noop, or callback
when a step item jumps Onbeforepanechange: $.noop,//steps before switching the callback
Onpanechange: $.noop,//steps after the handover of the content item
onpaneload: $.noop// The callback for the first time the step content item is displayed
;
The annotation section has been made clear, the first 5 in the implementation of ideas are related to the content mentioned. Let me give you a detailed description of the four callback functions and calls:
1) onstepjump (Oldstepindex, Targetstep)
This callback is triggered in a large step jump, the role is very clear, is to do some logic when the step jump, such as business requirements in the "3 Waiting for Audit" special effect control to use this callback, The physical index (starting from 0) of the steps before the jump is passed with two arguments oldstepindex, Targetstep represents the physical index of the step to jump to.
2) Onbeforepanechange (Currentpane, Targetpane, Currentstep)
The effect of this callback is to switch the small steps, the possible small steps in the form of a check failed, you have to cancel the small step of the switch, by returning false in this callback can achieve this effect. The three parameters passed are physical indexes, representing the position of the current small step, the small step position to toggle, and the position of the current large step.
3) Onpangechange (Currentpane, Targetpane, Currentstep)
This is similar to the second one, except that it occurs after the small step switch completes.
4) Onpaneload (E,currentstep, Currentpane)
This callback works very well, and other components within the small steps, such as form components, can be defined in this callback to implement the function of lazy initialization. At the same time, this callback has already pointed this to the DOM element of the current small step in execution, so that the DOM element can be quickly used to find the child elements needed by the other component initialization, such as form. This callback is only triggered once for each small step. Passing three parameter e indicates the related JQ event, followed by two representing the physical index of the current large step and small step respectively.
Callback Trigger Order is: Onbeforepanechange,onpangechange,onpaneload,onstepjump. In addition, the onpaneload is called once when the component initialization is complete.
These callbacks will basically solve the problems of the previous business requirements.
Looking at the API again, I consider only 3 API instance methods based on previous requirements:
return {
gostep:function (step) {
gostep (step-1);
},
gonext:function () {go
(Currentpane + 1);
},
goprev:function () {go
(currentPane-1);
}
}
Gostep can jump to the first step of the specified step, GoNext jumps to the next small step, and Goprev jumps to the previous step. There is also a static method:
Gets the position relative to the step item, based on the absolute index position of the step content item
//step from 0, pane represents the absolute index position, such as Steppanes total of 6, then pane possible value is 0-5
//example: config: [ 1,3,1,1], Step:2, Pane:4, will return 1, representing the position
Stepjump.getrelativepaneindex = function (config, step, pane) for the 1th step of the third step. return
pane-getpanecountbeforestep (config, step) + 1;
};
Because the parameters that are passed in the previous callback are physical indexes, this method must be used if the physical index is to be converted to a logical index on the outside.
Other detailed Description:
1) Maxstepindex
This variable is also critical, by which to control which big steps cannot be jumped by direct clicking.
2 UI Control for large step items
Step Item UI Control
function Showstep (targetstep) {
$navSteps. Each (function (i) {
var cname = this.classname;
CNAME = $.trim (Cname.replace (/current|done/g, ")");
if (I < targetstep) {
//the state before the current step is all set to done
cname + = ' done ';
} else if (i = = targetstep) {
//The current step item state is set to Current
cname + = ' current ';
}
This.classname = CNAME;}
);
The overall implementation is as follows, the degree of code optimization is limited by the level, but the logic is clear:
Define (function (Require, exports, module) {var $ = require (' jquery ');//step: Represents a step item//pane: Represents a step content item var DEFAULTS = {Con Fig: [],//Required parameters, the configuration of step items and step content items, such as [1,2,3] represents a total of three (config.length) steps, the 1th step has 1 (config[0) content items, and the 2nd step has 2 (config[1]) content items, The 3rd Step has 3 (config[2]) content items Steppanes: ',//required parameters, the JQ selector for the Step content item Navsteps: ',//required parameters, JQ selector initstepindex:1 for step items,//the step position to display at initial time, If there are a total of 4 steps, the parameter has the optional value: 1,2,3,4 initpaneindex:1,//////The position of the step content item initially displayed, based on Initstepindex, if Initstepindex is set to 2, and the step has 3 content items, The optional value of this parameter is: 1,2,3 onstepjump: $.noop,//callback for step item jump Onbeforepanechange: $.noop, the callback before the step content item Toggle Onpanechange: $.noop,//
Callback Onpaneload After the step content item switch: The callback when the $.noop//step content item is first displayed; function Stepjump (options) {var opts = $.extend ({}, DEFAULTS, options), $stepPanes = $ (opts.steppanes), $navSteps = $ (opt s.navsteps), config = opts.config, steppanecount = sum.apply (null, config),//Total number of step content items Currentstep = Opts.initstepindex- 1,//index of the current step item = sum.apply (null, Config.slice (0, Currentstep)) + (opts.initpaneindex-1),//index of the current content item Maxstepin Currentpane Dex = curRentstep,//allows the maximum step item position to be jumped by clicking the Step item directly $activePane = $stepPanes. EQ (currentpane); Registers a stepload event that triggers only once $stepPanes. each (function () {$ (this). One (' Stepload ', $.proxy (function () {opts.onPaneLoad.apply
(This, [].slice.apply (arguments). concat ([Currentstep, Currentpane]);
}, this));
});
Initialize UI Showstep (currentstep);
$activePane. addclass (' active '). Trigger (' stepload '); Registers a callback $navSteps. On (' Click.step.jump ', function () {var $this = $ (this), step = $this. Index (opts.navsteps);//Find the current point
The position of the keystroke in all step items if (Step > Maxstepindex | | $this. HASCLASS ("current") return;
Jump to the first step of the step item gostep (steps);
}); Step Item UI Control function Showstep (targetstep) {$navSteps. each (function (i) {var cname = this.classname; cname = $.trim (cname.re
Place (/current|done/g, ")"); if (I < targetstep) {//The state before the current step is all set to done cname + = ' done ';} else if (i = = Targetstep) {//Current step item status set to present CNAME =
' Current ';
} this.classname = CNAME;
}); function Gostep (step) {Go (Getpanecountbeforestep (config, step);}//Find the bit of the step item through the steps content itemSet function Getstepbypaneindex (Targetpane) {var r = 0, targetstep = 0; for (var i = 0; i < Steppanecount; i++) {R = R
+ Config[i];
if (Targetpane < r) {targetstep = i; break;}}
return targetstep; } function Go (targetpane) {if (Targetpane < 0 | | | | targetpane >= steppanecount) {return;}///To external callbacks before switching the step content item for external
You can do some validation on the current step content item///If callback returns false Cancel toggle var ret = Opts.onbeforepanechange (Currentpane, Targetpane, currentstep);
if (ret = false) return;
var $targetPane = $stepPanes. EQ (targetpane), Targetstep = Getstepbypaneindex (Targetpane);
$activePane. Removeclass (' active ');
$targetPane. addclass (' active ');
Opts.onpanechange (Currentpane, Targetpane, currentstep);
$activePane = $targetPane;
Currentpane = Targetpane;
var oldstepindex = Currentstep;
Currentstep = Targetstep;
Currentstep > Maxstepindex && (maxstepindex = currentstep);
$targetPane. Trigger (' stepload '); if (targetstep!== oldstepindex) {showstep (targetstep); Opts.onstepjump (Oldstepindex, Targetstep);}}
return {gostep:function (step) {gostep (step-1);}, Gonext:function () {Go (Currentpane + 1);}, Goprev:function () {
Go (currentPane-1); }///According to the absolute index position of the step content item, get the position relative to the step item//step starting from 0, pane represents the absolute index position, for example, Steppanes has 6, then the pane possible value is 0-5//example: config: [1,3,1,1], Step:2, Pane:4, returns 1, representing the position of the 1th step of the third step in the content item Stepjump.getrelativepaneindex = function (config, step, pane) {return Pane-ge
Tpanecountbeforestep (config, step) + 1;
}; Sum//NOTE: The data returned by Slice (start,end) does not contain the element function sum () {var a = [].slice.apply (arguments) of the end index, r = 0; A.foreach (function (
N) {r = R + N;});
return R; }//Statistics How many step items are there before the specified step item, which starts with 0, such as config: [1,3,1,1], which returns 4 function getpanecountbeforestep (config, step) when step=2.
{return sum.apply (null, config.slice (0, step));}
return stepjump; });
4. Call examples
How to use in demo.html:
Define (function (Require, exports, module) {
var $ = require (' jquery ');
var stepjump = require (' Components/stepjump '),
stepjump = new Stepjump ({
config: [1,3,1,1],
steppanes: ' # Step-content. Step-pane ',
navsteps: ' #steps > Li ',
initstepindex:1
});
$ (document). On (' Click.stepPane.switch ', '. Btn-step ', function (e) {
var $this = $ (this);
if ($this. Hasclass (' next ')) {
stepjump.gonext ();
} else {
stepjump.goprev ();}}
);
};
Since this is a static feature, there is no need to add any callbacks.
How to use in regist.html:
Step_status value://0 to fill in the data, if each entry to this page is the state value, then show the "1 check-in notice" This big step, indicating that the step is underway;//1 to submit the information, if each entry to this page is the state value, then show "2 Company Information Submission "This big step, the small step defaults to show it's first;//2 audit failed, if each entry to this page is the state value, then show" 3 Waiting for audit "this big step;//3 Audit has passed, if each entry to this page is the status value, then show" 3 Waiting for Audit " This big step;//4 to confirm the contract, if each entry to this page is this state value, then show the "4 contract signed" this big step; var step_status = 3, MODE = Step_status = 2 | | Step_status = 4? 3:2,//3 is read-only, in the Company information submission steps can only see can not be changed step_audit_alerts = Step_status = = 3,//This variable is used to control whether to add a ALERTS style to the step item when waiting for the audit step Step_status_ MAP = {0:1, 1:2, 2:3, 3:3, 4:4}, Initstepindex = Step_status_map[step_status], step_panes_data = [1, 3, 1, 1], step _panes_config = {//3,1 represents the first step of the third step content 3: {1: {onpaneload:function (E, Currentstep, Currentpane, conf) {var $stepPane =
$ (this);
CONF.VC = new Visiblecontroller ($stepPane. Children (' div ')); if (step_audit_alerts) {$auditStep = $ (' #audit-step '); $auditStep. addclass (' ALERTS '); Conf.vc.show (' #audit-no '); else if (step_status = 2 | | Step_status = = 4) {conf.vc.show (' #audit-yes ');} else {conf.vc.show (' #aUdit-wait '); }}, Onleavestep:function () {step_audit_alerts && $auditStep. Removeclass (' ALERTS '); Onenterstep:function (
Step, conf {if (step_audit_alerts) {$auditStep. addclass (' ALERTS ');} else {conf[1].vc.show (' #audit-wait ');}}, 4: { 1: {onpaneload:function (E, Currentstep, Currentpane, conf) {var $stepPane = $ (this); conf.vc = new Visiblecontroller ($
Steppane.children (' div '));
Conf.vc.show (' #contract-confirm '); }}, Get_step_panes_config = function (step, pane) {if (pane = = undefined) return step_panes_config[step + 1]; Step_panes_config[step + 1] && Step_panes_config[step + 1][stepjump.getrelativepaneindex (STEP_PANES_DATA,
Step, pane)];
}; var $auditStep, stepjump = new Stepjump ({config:step_panes_data, steppanes: ' #step-content. Step-pane ', navsteps: ' #step s > Li ', initstepindex:initstepindex, Onbeforepanechange:function (Currentpane, Targetpane, CurrentStep) {var conf =
Get_step_panes_config (Currentstep, Currentpane); Return conf &Amp;& conf.onbeforepanechange && conf.onBeforePaneChange.apply (This, [].slice.apply (arguments). concat[
CONF]); }, Onpanechange:function () {window.scrollto (0, 0);}, Onpaneload:function (E, Currentstep, Currentpane) {var conf = get
_step_panes_config (Currentstep, Currentpane);
conf && conf.onpaneload && conf.onPaneLoad.apply (This, [].slice.apply (arguments). Concat ([conf])); }, Onstepjump:function (Currentstep, Targetstep) {var conf = get_step_panes_config (CURRENTSTEP); Conf && Conf.on
Leavestep && Conf.onleavestep (currentstep, conf);
conf = Get_step_panes_config (TARGETSTEP);
conf && conf.onenterstep && conf.onenterstep (targetstep, conf); }
});
The initialization of the Stepjump component is on the last side, preceded by some configuration-related content. Replacing the value of Step_staus this variable can simulate the different business states in the actual business, and you can see the effect of the display of the component when different states enter the page.
5. Summary
This article summed up some of the recent work, provided a stepjump component, perhaps in your work also have a need, of course, everyone's ideas and practices are not necessarily the same, I just share the purpose. In fact, these days the work of thinking things are still quite a lot of, in addition to this component, more ideas are focused on style separation, CSS naming and form components of the separation of this piece, but now these ideas are not enough system, not to summarize the level of sharing, these methods of work theory, few people to sum up and share, I currently only see Rok Zhang Xinsen Xu's blog has a more complete set of ideas, study down, there are a lot of harvest and experience, but this is someone else, there are some sensed unspeakable essence, or not mastered, can only step by step to accumulate the line, and so on the future of my own thinking, I will consider all my ideas to share out And I believe this will be the most valuable thing I have shared this year.
The above content for you to introduce a multi-step JS stepjump components, I hope to help you!