This article mainly introduces detailed information about step-by-step StepJump component instances in JS. If you need more information, refer to the latest work to create a multi-step, multi-step form page, this multi-step, multi-step process means that the services on this page are completed in multiple steps, and each step may be processed in multiple small steps, and between small steps are a sequential business relationship. At first, I thought this function was very easy to do, just like the implementation principle of the tab page. I realized it only after I did it, there are still a lot of related logic here (maybe I didn't expect a better solution ~), Especially when this function is combined with the form and business data status. I abstracted some logics related to this function into a component StepJump, which can implement pure static step-by-step switching and redirection, as well as the complex logic combined with the business, it has certain versatility and flexibility. This article mainly introduces its functional requirements and implementation ideas.
Effect:
There are two effects pages: demo.htmland regist.html. The relevant js is demo. js and regist. js respectively. The components are encapsulated in stepJump. js and seajs is used for modularization. The demo of demo.htmlis a pure static multi-step and multi-step internal transformation. regist.html is a complete combination of services. It is extracted from my recent work, but the business data status is simulated using a constant (STEP_STATUS.
1. Requirement Analysis
The preceding information is incomplete, but the design drawing is too large to be pasted. In order to clearly describe the functional requirements of this page, I have to spend as much effort on text as possible and try to make every detail clear:
1) This page has a total of four major steps, of which 1, 3, 4 correspond to only one small step, and 2 corresponds to three small steps, that is, 1, 3, 4. One step can be completed, and two steps can be completed;
2) these steps are the relationship between the occurrence of order. The 1st stride must be completed before the 2nd stride can be taken. The 1st stride must be completed before the 2nd stride can be taken;
3) the first small step of each major step may have a button that can return to the previous step;
4) there may be two buttons for a small step in the middle of each big step. One button returns the previous small step and the other one jumps to the next small step;
5) The last small step of each big step may have a button that can jump to the next big step;
6) if a large step contains only one small step, it is both the first and last small step;
7) the content of each small step in each big step is different. Only one small step can be displayed at a time;
8) the major steps that have been completed, the major steps in progress, and the major steps to be executed later should have different UI effects (however, in terms of implementation effects, the completed and ongoing operations make an effect)
9) You must click the button in the last step of the previous step to jump to the next step. You can click the name of the completed or ongoing step to jump to the next step;
10) Click the big step name to jump to the first small step of the big step.
The above section is the static Function Analysis of the page. The following section analyzes the actual business needs of the page:
1) This page is open for login users. It is used to apply for a user's entry into a platform. Only after the entry process is completed can the platform officially access other features;
2) The main business data is related to the user. According to the check-in process, the status of the user's check-in process can be divided:
A. Information to be filled in. If this status value is displayed every time you enter this page, the [1 check-in instructions] Step is displayed, indicating that this step is in progress;
B. The information to be submitted. If this status value is displayed every time you enter this page, the big step [2 submit company information] IS DISPLAYED. The first step is displayed by default;
C. The review fails. If this status value is displayed every time you enter this page, the "3 waiting for review" step is displayed;
D. The review has passed. If this status value is displayed every time you enter this page, the "3 waiting for review" step is displayed;
E. To confirm the contract, if this status value is displayed every time you enter this page, the [4 Contract Signing] Step is displayed;
3) It should be noted that [3 awaiting review] and [4 Contract Signing] each contain three and two items, each of which is mutually exclusive, however, they are not step-by-step relationships. The specific display of which is completely determined by the service status. For example, [3 awaiting review] has the following three possible effects:
When you jump from [2 company information submission] to [3 waiting for review], the first result is displayed;
If the page displays 2nd results in the approved status;
If the page displays 3rd results in a status that is not approved, and in this case, the step name has special effect requirements:
When you click the step name directly, for example, click [2 company information submission], the effect will be restored to the default effect. Then click [3 awaiting review ], you must set this special effect. This special effect can be completely revoked only when you jump to [3 waiting for review] through the small step of [2 company information submission.
The general requirement is the above parts, and some details may not be described. Because it is not easy to make it clear in words, it can only be understood based on the actual results. In the final implementation, static functional requirements are the core of component implementation in the preceding requirements, and subsequent business requirements are not universal, the starting point of my development of this component is to write the basic functions of the component according to the static functional requirements, and then design a reasonable API and callback based on the business needs, and try to separate js and html as much as possible, components are separated from services to maximize flexibility.
2. Implementation ideas
First, let's talk about the html structure: I think there are two important concepts in the previous requirements: big steps, small steps, and the relationships between these big steps and small steps, there are order conventions between steps, so we have to design two sets to store all major step-related items and all small step-related items respectively. The html structure can be designed as follows:
-Hide code
- 1 check-in instructions
- 2. Submit company information
- 3. awaiting review
- 4. Contract Signing
Here, # steps li is all big step items, and all # step-content. step-pane is all small step items. These two sets only solve the storage and order of step items, and the inclusion relationship between them has not yet been solved. The main steps and small steps are as follows:
In this case, we only need to use a simple configuration array to reflect this relationship. For example, we can use [,] to describe the above structure, indicating that there are four major steps, here, 1, 3, and 4 have only one small step, and 2 has three small steps. Because the large and small steps are stored in two sets separately, when we access these two sets, we use the index location of the relative set, however, in actual use, the location of the big step is better identified, and the absolute location of the small step is not easy to recognize, and the relative position of the set starts from 0, if other components are defined in the content of each small step, such as form-related components, we will certainly store the instances of these components in a configuration table:
var STEP_CONFIG = {0: {form: {//....}},1: {form: {//....}},2: {form: {//....}},3: {form: {//....}}//...}
This type of configuration table is identified by an absolute index in a small step, which is inconvenient for actual use. When the html structure of a small step is adjusted, the structure must be changed, all the places that reference it to obtain related components must be modified. The following configuration method is recommended:
Var STEP_PANES_CONFIG = {// indicates the first small step of the Second Step. // indicates the second small step of the Second Step. // indicates the third small step of the second step. 2: {1: {// configure things related to the small step}, 2: {// configure things related to the small step}, 3: {// configure things related to small steps} // configure things related to large steps }}
It is equivalent to abstracting the preceding include structure:
This structure has two advantages: first, the collection index starts from 0, and STEP_PANES_CONFIG [2] indicates the 2nd big step; second, the indexing of small steps does not consider starting from 0, and is identified by relatively large steps, for example, STEP_PANES_CONFIG [2] [1] indicates the first small step of the 2nd big step. In this way, both the big step and the small step can be identified through indexes, the configuration table is also more stable. That is to say, when the component provides index-related interfaces or parameters externally, it is provided in the conventional way of thinking. The logical index must be resolved within the component (for example, [2] [1]). the Conversion Relationship between physical indexes and logical indexes. For example, when an external call tells the component that the initialization needs to display the 2nd small step of the 1st stride, the component must find the corresponding size and step items based on this information; when the physical index location of the external step item is known, the component must provide a method to convert the physical index location into a logical index.
Effect:
1) You only need to control the display content of each step. Therefore, if css is used to control the process content, there are only two States. The default and active states are not displayed, active State display;
2) You can use css borders to draw triangles at the corners of each step;
3) In order to correctly control the effect of the step, if css is used for each step to control the State, there are three default states: done and current, indicating that the State is not executed, respectively, executed and ongoing steps. There is also an alerts state in the third step, but this is a state related to the business, but it has nothing to do with components. The implementation of these three states is similar to the online rating component.
The general idea is this. In addition, I will describe the API and callback design in the implementation details in the next section.
3. Implementation Details
Let's take a look at the configuration items of the component:
Var DEFAULTS = {config: [], // required parameter. configuration of step items and step content items, such as [1, 2, 3] indicates a total of three (config. length) Step, 1st steps have 1 (config [0]) content item, 2nd steps have 2 (config [1]) content items, step 3 (config [2]) content item stepPanes: '', // required parameter. The jq selector navSteps:'' of the Step content item :'', // required parameter. The jq selector of the Step item is initStepIndex: 1, // The initial step position displayed. If there are four steps in total, the optional value of this parameter is: 1, 2, 3, 4 initPaneIndex: 1, // The Position of the Step content items displayed at the initial time. Based on initStepIndex, if initStepIndex is set to 2, and this step has three content items, the optional values of this parameter are: 1, 2, 3 onStepJump: $. noop, // callback onBeforePaneChange: $. noop, // The callback onPaneChange before switching the step content item: $. noop, // The onPaneLoad callback after the step content item is switched: $. noop // The callback for the first display of step content items };
The comments have been quite clear, and the first five are mentioned in the Implementation ideas. Below I will give a detailed description of the four callback functions and calls:
1) onStepJump (oldStepIndex, targetStep)
This callback is triggered when a large step is redirected, and the function is very clear, that is, to perform some logic processing during the step jump, for example, you need to use this callback to control the special effects of [3 awaiting review] in your business requirements. Two Parameters oldStepIndex are passed to indicate the physical index of the previous step (starting from 0 ), targetStep indicates the physical index of the step to jump.
2) onBeforePaneChange (currentPane, targetPane, currentStep)
The function of this callback is to cancel the switchover of a small step when the form verification in the small step fails, this effect can be achieved by returning false in this callback. The three parameters passed are physical indexes, indicating the location of the current small step, the location of the small step to be switched, and the location of the current large step.
3) onPangeChange (currentPane, targetPane, currentStep)
This is similar to the second one, but it is called after the small step switch is complete.
4) onPaneLoad (e, currentStep, currentPane)
This callback is very useful. Other components in the small step, such as form components, can be defined in this callback to implement the delayed initialization function. At the same time, this callback points this to the DOM element corresponding to the current small step, so that you can quickly find the child elements required for initialization by other components through this DOM element, for example, form. This callback is triggered only once for each small step. Pass the three parameters e to indicate the relevant jq events, and the next two represent the physical indexes of the current large step and the small step respectively.
The callback trigger sequence is onBeforePaneChange, onPangeChange, onPaneLoad, and onStepJump. In addition, onPaneLoad is called once when the component Initialization is complete.
The above callbacks can basically solve the problems of the previous business needs.
Let's take a look at the API. I only consider three API instance methods based on the 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 small step of the specified step. goNext will jump to the next small step, and goPrev will jump to the previous small step. There is also a static method:
// Obtain the absolute index position relative to the step item based on the absolute index position of the step item. // step starts from 0, and pane indicates the absolute index position. For example, there are 6 stepPanes in total, the possible pane value is 0-5 // For example: config: [,], step: 2, pane: 4, 1 is returned, it indicates the position of step 1st in step 3. getRelativePaneIndex = function (config, step, pane) {return pane-getPaneCountBeforeStep (config, step) + 1 ;};
Because the parameters passed in the preceding callback are physical indexes, this method must be used if physical indexes need to be converted to logical indexes externally.
Other details:
1) maxStepIndex
This variable is also critical. It is used to control which major steps cannot be redirected by clicking directly.
2) UI control of big 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) {// all the statuses before the current step are set to donecname + = 'done ';} else if (I = targetStep) {// the status of the current step item is set to currentcname + = 'current';} this. className = cname ;});}
The overall implementation is as follows. The degree of code optimization is restricted by the level, but the logic is still clear:
Define (function (require, exports, module) {var $ = require ('jquery '); // step: indicates the step // pane: var DEFAULTS = {config: [], // required parameter. The configuration of step and step content items, such as [1, 2, 3] indicates a total of three (config. length) Step, 1st steps have 1 (config [0]) content item, 2nd steps have 2 (config [1]) content items, step 3 (config [2]) content item stepPanes: '', // required parameter. The jq selector navSteps:'' of the Step content item :'', // required parameter. The jq selector of the Step item is initStepIndex: 1, // The initial step position displayed. If there are four steps in total, the optional value of this parameter is: 1, 2, 3, 4 initPaneIndex: 1, // The Position of the Step content item displayed at the initial time, Based on initStepIndex, if initStepIndex is set to 2 and this step has three content items, the optional values of this parameter are: 1, 2, 3 onStepJump: $. noop, // callback onBeforePaneChange: $. noop, // The callback onPaneChange before switching the step content item: $. noop, // The onPaneLoad callback after the step content item is switched: $. noop // callback for the first display of step content items}; function StepJump (options) {var opts =$. extend ({}, DEFAULTS, options), $ stepPanes =$ (opts. stepPanes), $ navSteps = $ (opts. navSteps), config = opts. config, stepPaneCount = sum. apply (null, config ), // The total number of step content items currentStep = opts. initStepIndex-1, // index of the current step currentPane = sum. apply (null, config. slice (0, currentStep) + (opts. initPaneIndex-1), // index of the current content item maxStepIndex = currentStep, // you can directly click the maximum position of the Step item to jump to $ activePane = $ stepPanes. eq (currentPane); // register only one stepLoad event $ stepPanes. each (function () {$ (this ). one ('stepload', $. proxy (function () {opts. onPaneLoad. apply (this, []. slice. apply (arguments ). conca T ([currentStep, currentPane]) ;}, this) ;}); // initialize UIshowStep (currentStep); $ activePane. addClass ('active '). trigger ('stepload'); // registers the callback for clicking a step item $ navSteps. on ('click. step. jump ', function () {var $ this = $ (this), step = $ this. index (opts. navSteps); // locate the position of the currently clicked step item in all step items if (step> maxStepIndex | $ this. hasClass ('current') return; // jump to the First step content item goStep (step) ;}) of this step item; // step item UI controls function showStep (targetStep) {$ navSteps. Each (function (I) {var cname = this. className; cname = $. trim (cname. replace (/current | done/g, ''); if (I <targetStep) {// all the statuses before the current step are set to donecname + = 'done ';} else if (I = targetStep) {// the status of the current step item is set to currentcname + = 'current';} this. className = cname;});} function goStep (step) {go (getPaneCountBeforeStep (config, step ));} // find the position of the Step item through the step content item function getStepByPaneIndex (targetPane) {var r = 0, targetStep = 0; f Or (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 ;}// callback provided to the outside before switching the step content items, this allows you to check the content items in the current step. // If the callback returns false, cancel the var ret = opts switch. onBeforePaneChange (currentPane, targetPane, currentStep); if (ret = false) return; var $ targetPane = $ stepPanes. eq (t ArgetPane), 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) ;}}// based on the absolute index position of the Step content, obtain the position from 0 to the position of the step item. pane indicates the absolute index position. For example, if stepPanes has six locations, the possible pane value is 0-5. // example: config: [1st,], step: 2, pane: 4, 1 is returned, indicating the position of step in step 3. getRelativePaneIndex = function (config, step, pane) {return pane-getPaneCountBeforeStep (config, step) + 1 ;}; // sum // Note: slice (start, end) the returned data does not contain the end index element function sum () {var a = []. slice. apply (arguments), r = 0;. forEach (function (n) {r = r + n ;}); return r ;}// count the total number of step content items before the specified step item, step starts from 0, for example, config: [,]. When step is 2, 4 function getPaneCountBeforeStep (config, step) {return sum is returned. apply (null, config. slice (0, step);} return StepJump ;});
4. Call example
Demo.html usage:
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();}});});
This is a static function, so no callback is required.
How to Use regist.html:
// STEP_STATUS value: // 0. If this status value is displayed every time you enter this page, the step [1 check-in instructions] is displayed, indicates that this step is in progress; // 1. If this status value is displayed every time you enter this page, the large step [2. Submit company information] is displayed, the first review is displayed in step 2 by default. // if this status value is displayed every time you enter this page, the step [3 waiting for review] is displayed; // 3 the review has passed. If this status value is displayed every time you enter this page, the [3 waiting for review] Step is displayed. // 4 the contract to be confirmed, if this status value is displayed every time you enter this page, the big step [4 Contract Signing] is displayed; var STEP_STATUS = 3, MODE = STEP_STATUS = 2 | STEP_STATUS = 4? 3: 2, // 3 indicates read-only. In the company information submission step, you can only check whether STEP_AUDIT_ALERTS = STEP_STATUS = 3 can be changed, // This variable is used to control whether to add the alerts style STEP_STATUS_MAP = {0: 1, 1: 2, 2: 3: 3, 4: 4} to the step item while waiting for the review step }, initStepIndex = STEP_STATUS_MAP [STEP_STATUS], STEP_PANES_DATA = [1, 3, 1, 1], STEP_PANES_CONFIG = {// 3, 1 indicates the first step of Step 3: {1: {onPaneLoad: function (e, currentStep, currentPane, conf) {var $ stepPane = $ (this); conf. vc = new VisibleController ($ stepPane. children ('P'); 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 ('P'); conf. vc. show ('# contract-confirm') ;}}}, GET_STEP_PANES_CONFIG = function (step, pane) {if (pane = undefined) return STEP_PANES_CONFIG [step + 1]; return 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: '# steps> li', initStepIndex: initStepIndex, onBeforePaneChange: function (currentPane, targetPane, currentStep) {var conf = GET_STEP_PANES_CONFIG (currentStep, currentPane ); return conf & 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. onLeaveStep & conf. onLeaveStep (currentStep, conf); conf = GET_STEP_PANES_CONFIG (targetStep); conf & conf. onEnterStep & conf. onEnterStep (targetStep, conf );}});
The initialization of the StepJump component is at the end, with configuration-related content in the front. Change the value of STEP_STAUS to simulate different service statuses in the actual business. Then, you can see the effect of this component when different statuses enter the page.
5. Summary
This article summarizes some of the results of recent work and provides a StepJump component, which may also be useful in your work. Of course, everyone's ideas and practices are not necessarily the same, I just want to share it. In fact, there are a lot of things to think about in the past few days. In addition to this component, more ideas are concentrated on style separation, and CSS naming and form component separation, however, these ideas are not systematic enough and cannot be summarized and shared. Few people summarize and share these theories at the working method level, currently, I have only seen Zhang xinxu's blog with a complete set of ideas. After learning this, I have gained a lot of experience and experience. But after all, this is what others do, and there are some things that can't be said, I still can't grasp it. I can only accumulate it step by step. When my own ideas will take shape in the future, I will consider sharing all my ideas, I believe this will become the most valuable content I have shared this year.
The above content introduces the multiple step-by-step StepJump component in JS. I hope it will be helpful to you!