2
\$\begingroup\$

I am creating a step-by-step form (it only shows in step-by-step and is not a multi state form) and the parts slide in and out (similar to GitHub but without the browser's back buttons). I have written the following code to implement this. Could you please review my code and suggest flaws:

$(document).ready(function(){

  // when the div with text next is clicked
  $('.nextButton').click(function(){

    // remove the class from div that is visible at present, add it the class for divs on the left side and move it to the left side 
    // and remove from the next div, the class assigned divs lying to the right and add bring it in the visible area and assign visible class to it

    $('.visible').removeClass('visible').addClass('onLeft').animate({left:'-980px'}).next('div').removeClass('onRight').animate({left:'0px'}).addClass('visible');

    //fade in the div with title previous  
    $('.prevButton').fadeIn();

    //if there are no div left on the right side remove the class for next button and add the class for done button and modify the HTML to done
    if($('.onRight').length==0)
    {
      $(this).removeClass('nextButton').addClass('doneButton').html('Done');
    }
  });

  //when the div with text previous is clicked
  $('.prevButton').click(function(){

    //if the div with class 'doneButton' exists, i.e we are at the last step, then remove the class and assign the div class for next button and text next
    if($('.doneButton').length)
    {
      $('.doneButton').removeClass('doneButton').addClass('nextButton').html('Next');           
    }

    //if there is just 1 div left towards left, i.e. we are on the 2nd step, remove  the div with title previous
    if($('.onLeft').length==1)
    {
      $(this).fadeOut();
    }

    //remove the class from div that is visible at present, add to it the class for divs on right side and move it to the right side, remove 
    //the class assigned to divs on the left from the left div and add visible class to the div and bring it in the visible section
    $('.visible').removeClass('visible').addClass('onRight').animate({left:'980px'}).prev('div').removeClass('onLeft').animate({left:'0px'}).addClass('visible');
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>

<div class="formContent" style="width:980px;height:100px;float:left;overflow:hidden;position:relative;">
                <div class="visible bt" style="width:980px;float:left;position:absolute;">Part1</div>
                <div class="onRight bt" style="width:980px;float:left;position:absolute;left:980px;">Part2</div>
                <div class="onRight bt" style="width:980px;float:left;position:absolute;left:980px;">Part3</div>
                <div class="onRight bt" style="width:980px;float:left;position:absolute;left:980px;">Part4</div>
            </div>
            <div class="formButtons" style="width:980px;height:50px;float:left;">
                <div class="nextButton" style="float:right">Next</div>
                <div class="prevButton" style="float:right;margin-right:20px;display:none;">Previous</div>
            </div>

(Note that the "Next" button is quite far to the right in this demo.)

\$\endgroup\$
  • \$\begingroup\$ The code is working fine BTW :) But still, I feel that using so many methods in just a single statement shouldn't be the right approach, because I have never seen anyone use it so far. \$\endgroup\$ – gentrobot May 9 '12 at 13:54
  • \$\begingroup\$ Use the second context parameter to the jquery function. Its almost always the right move, it will hugely speed up your code and help prevent bugs due to naming conflicts. In this case it seems like your context should be the above form or at least $('div.formContent') \$\endgroup\$ – George Mauer May 9 '12 at 15:57
1
\$\begingroup\$

Did some mods with the HTML. You should separate classes for targetting and classes for state. You can use data-* to keep states also, just so you know. But for this one, I used leftButton and rightButton classes for targetting and left prevButton, nextButton and doneButton for the state.

<div class="formButtons" style="width:980px;height:50px;float:left;">
    <div class="nextButton rightButton" style="float:right">Next</div>
    <div class="prevButton leftButton" style="float:right;margin-right:20px;display:none;">Previous</div>
</div>​

As for the JS, it's pretty much commented. And jQuery was designed to be chained. However, you have to know what is returned in the previous call so that you can optimize the chain.

//shortened doc ready
$(function() {

    //cache the buttons. they never go away so set them statically
    //added an additional class for targetting, instead of the 
    //prev, next and done states
    var rightButton = $('.rightButton');
    var leftButton = $('.leftButton');

    //delegation using on()
    $('.formContent').on('click', '.rightButton', function() {

        //use the value as boolean
        if (!$('.onRight').length) {
            $(this)                        //you can get away with $(this) 
                .removeClass('nextButton') //because it's only used once
                .addClass('doneButton')
                .text('Done');             //use text if you are only putting text              
        }

        leftButton.fadeIn();

        $('.visible')                      //i usually newline and indent
            .removeClass('visible')        //anything that pertains to the current selection
            .addClass('onLeft')            //moved addClass up for readability only if it's for state.
            .animate({                     //if it's used for style
                left: '-980px'             //move it back below animate
            })                             
            .next()                        //you can omit the "div" since they are all divs
                .removeClass('onRight')    //indentation again
                .addClass('visible')
                .animate({
                    left: '0px'
                });


    }) //chain the next handler
    .on('click', '.leftButton', function() {

        //cache donebutton since it's used twice
        var doneButton = $('.doneButton');

        if (doneButton.length) {
            doneButton.removeClass('doneButton')
                .addClass('nextButton')
                .text('Next'); //use text if you are only putting text
        }

        //use the value as boolean
        if ($('.onLeft').length) {
            $(this).fadeOut();
        }

        $('.visible')
            .removeClass('visible')
            .addClass('onRight')
            .animate({
                left: '980px'
            })
            .prev() //same as before, omit the div
                .removeClass('onLeft')
                .addClass('visible')
                .animate({
                    left: '0px'
                });
    });
});​
\$\endgroup\$
3
\$\begingroup\$

jQuery was designed so you can chain function calls together like that. If you're worried about readability, then break them up into logical parts. You can either use variables, or keep the chaining and use whitespace to split them up. Instead of:

$('.visible').removeClass('visible').addClass('onLeft').animate({left:'-980px'}).next('div').removeClass('onRight').animate({left:'0px'}).addClass('visible');

you could do something like this:

$('.visible')
  .removeClass('visible')
  .addClass('onLeft')
  .animate({left:'-980px'})
.next('div')
  .removeClass('onRight')
  .animate({left:'0px'})
  .addClass('visible');
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.