📜 ⬆️ ⬇️

Registration on the site: from the beginning to the afternoon

Hi habr!
I work in the field of web development and the other day I had an interesting task - it was necessary to create a complex registration form, which will have twenty-two (22!) Text fields and one large list with checkboxes.
I have always been guided by the principle that a large number of fields on the registration form are very unpleasant for users, even if they receive material bonuses upon completion. Therefore, I always tried to make the form as simple as possible, with a maximum of 4 fields, if this was possible on the part of the business (customer). And even neglected the field with the captcha, using instead the hidden js-captcha, or completely abandoned it. But all attempts to bring it to the customer were unsuccessful.

Under habrakat I will try to create the most versatile solution to such problems


The first thought was that all polymers disappeared, but after a little more thought it became obvious that many fields could be divided into groups, and some could be removed altogether. As a result, I got 4 forms:
')

Now it is necessary to arrange it somehow for the users, excluding the occurrence of a manic impulse and immediate closing of the window on the appearance of all these fields and forms. It is best to do the filling of the fields in stages, form by form. And it is also desirable to make a roadmap (map), which clearly shows what to fill out, at what stage I am now and how much I have left.
Googling, I did not find anything suitable and began to think about how to adjust the finished image slider (instead of images use a div with fields). But I really wanted to draw a roadmap with a slider, which, as the forms are filled in, will move forward until it reaches the end. And it would be nice to provide a non-linear movement of the slider - so that you can jump over points if you do not want to fill out some form at all.
After experimenting with different sliders, I completely abandoned this idea - I decided to create my own, brand new and wonderful bike. Let it be with square wheels, since The experience of developing plug-ins is zero, but it will be 100% satisfying the task, contain nothing superfluous and, most importantly, it may be necessary for someone else.
So, a wrapper for the plugin, let it be called a roadmap .

(function ($) { $.fn.Roadmap = function (){ }; })(jQuery); 


Fine. Now we will add the ability to customize some plugin options (before initializing it), consider several public methods and add a closure so that we can use the jQuery functions for our object.

 (function ($) { $.Roadmap = $.Roadmap || {}; $.extend($.Roadmap, { extend: function (methods) { $.extend($.fn.Roadmap, methods); $.fn.extend(methods); } }); //    $.fn.Roadmap = function (options) { var options = $.extend({ onInit: null, allowJump: true, voyagerSpeed: 300, voyagerPosition: 0, checkpoints: [], onloadEvent: null, ckeckpointNext: null, checkpointPrev: null, width: 400, }, options); //   // $.Roadmap.extend,    $.Roadmap.extend({ CurrentPosition: function () { }, MoveNext: function () { }, MovePrev: function () { } }); //,    \ jQuery,  $.animate() return this.each(function () { //  }); }; })(jQuery); 


Is done. But the plug-in code is somehow lost, it is better to put it in a separate place, it will be more readable and not forget to describe the public functions. And so it was quite readable, all in the spoiler.

plugin code completely
 (function ($) { $.Roadmap = $.Roadmap || {}; $.extend($.Roadmap, { extend: function (methods) { $.extend($.fn.Roadmap, methods); $.fn.extend(methods); } }); $.fn.Roadmap = function (options) { var options = $.extend({ onInit: null, // ,    allowJump: true, //   , .  voyagerSpeed: 300, //      checkpoint voyagerPosition: 0, //   checkpoints: [], //    callbacks ckeckpointNext: null, //callback    (  MoveNext()) checkpointPrev: null, //  width: 400, //    }, options); var $roadmap = $("<div>").addClass("roadmap"), $voyager = $("<div>").addClass("voyager"), voyagerPosition = options.voyagerPosition, voyagerOffset = -1; //private  (  ,  ) var methods = { determineWidth: function ($obj) { var r = /[px|em]{2,}/g, w = 0; w += parseInt($obj.css("padding-left").replace(r, "")); w += parseInt($obj.css("padding-right").replace(r, "")); w += parseInt($obj.css("border-left-width").replace(r, "")); w += parseInt($obj.css("border-right-width").replace(r, "")); w += parseInt($obj.css("margin-left").replace(r, "")); w += parseInt($obj.css("margin-right").replace(r, "")); return w; } }; // ,   var make = function () { $(this) .css("width", options.width) .css("display", "none"); var $mark = $("<div>").addClass("mark"), $map = $("<div>").addClass("map"), $checkpoint = {}; $(options.checkpoints).each(function (i, o) { $checkpoint = $("<div>").addClass("checkpoint"); $checkpoint .append($("<div>")) .click(function (e) { if ((options.allowJump || !e.originalEvent) && $(e.target).closest(".voyager").length == 0) { var ts = $(this), tsOffset = ts.offset(), rmOffset = $roadmap.offset(); voyagerPosition = i; if (voyagerOffset < 0) { voyagerOffset = $voyager.offset().left; $voyager.css("left", voyagerOffset); } $voyager.animate({ left: (voyagerOffset + tsOffset.left - rmOffset.left - parseInt($map.css("padding-left"))) }, 400); $("div.mark") .find("div.marklabel").removeClass("active").end() .find("div.marklabel:eq(" + i + ")").addClass("active"); if (o.hndl != null && typeof (o.hndl) === "function") { o.hndl(voyagerPosition); } } }); $map.append($checkpoint); if (i < options.checkpoints.length - 1) { $map.append($("<div>").addClass("road")) $mark .append($("<div>").addClass("marklabel").html(o.text)) .append($("<div>").addClass("road")); } else { $map.append($("<div>").addClass("clear")); $mark.append($("<div>").addClass("marklabel").html(o.text)) } }); $roadmap .append($map) .append($mark); $(this).append($roadmap); var roadLength = 0, checkpointsTotalLength = 0; checkpointsTotalLength += methods.determineWidth($checkpoint.find("div")); checkpointsTotalLength += methods.determineWidth($checkpoint); roadLength = Math.floor((options.width - checkpointsTotalLength * 4) / 3); roadLength -= methods.determineWidth($map); roadLength -= methods.determineWidth($(".road", $map)); $map .find(".road").width(roadLength).end() .find(".checkpoint").eq(voyagerPosition).prepend($voyager).end(); $mark .find(".marklabel:eq(" + voyagerPosition + ")").addClass("active").end() .find(".marklabel").width(checkpointsTotalLength).end() .find(".road").width(roadLength).end(); if (options.onInit != null && typeof (options.onInit) === "function") { options.onInit(); } $(this).show(); $map.find(".checkpoint").eq(voyagerPosition).trigger("click"); }; //public ,      $.Roadmap.extend({ CurrentPosition: function () { return voyagerPosition; }, MoveNext: function () { if (voyagerPosition + 1 < options.checkpoints.length) { ++voyagerPosition; $("div.roadmap .checkpoint:eq(" + voyagerPosition + ")").trigger("click"); if (typeof (options.ckeckpointNext) === "function") { options.ckeckpointNext(voyagerPosition); } } }, MovePrev: function () { if (voyagerPosition - 1 >= 0) { --voyagerPosition; $("div.roadmap .checkpoint:eq(" + voyagerPosition + ")").trigger("click"); if (typeof (options.ckeckpointPrev) === "function") { options.ckeckpointPrev(voyagerPosition); } } } }); return this.each(make); }; })(jQuery);//  $.Roadmap.extend({ CurrentPosition: function () { return voyagerPosition; }, MoveNext: function () { if (voyagerPosition + 1 < options.checkpoints.length) { ++voyagerPosition; $("div.roadmap .checkpoint:eq(" + voyagerPosition + ")").trigger("click"); if (typeof (options.ckeckpointNext) === "function") { options.ckeckpointNext(voyagerPosition); } } }, MovePrev: function () { if (voyagerPosition - 1 >= 0) { --voyagerPosition; $("div.roadmap .checkpoint:eq(" + voyagerPosition + ")").trigger("click"); if (typeof (options.ckeckpointPrev) === "function") { options.ckeckpointPrev(voyagerPosition); } } } }); 



It remains to insert it on the page, for example
 $("#rmp").Roadmap({ checkpoints: [{ text: " ", }, { text: " }] }); 


Plugin available on github
Demo can be seen here.

UPD
Following the advice of ilvar , I moved the demo to pages.github.

PS
I used $ .animate () to move $ voyager around the map, refusing to use CSS3. The reason was the strange behavior of the $ .css ("- webkit-transform", "translateX (100)") construction in one of the latest versions of Chromium (28.0.1482.0 (194616)), although in the next release everything worked.

Pps
The plugin is currently not ready for commercial use and needs to be refined. Positioning errors in different browsers, smooth change of forms, use of a template engine in an array of checkpoints, ajax, form pull-up, etc. ... But not this year, I'm leaving for vacation)

Happy New Year 2014!

Source: https://habr.com/ru/post/207304/


All Articles