Mastering jQuery UI
上QQ阅读APP看书,第一时间看更新

Making the tour functional

Now that the layout of our page is complete, let's discuss in brief how we are going to implement the tour functionality. We have already created a tour.js file that we will use for writing all the JavaScript code.

We will initialize the accordion for which we have already written the markup, and we will also initialize the tooltip that will appear on hovering over the Contact Us link.

After that, we will define a jQuery UI dialog component with some basic settings and buttons for navigating the tour. Next, we will define a JavaScript array that will contain all the steps of the tour. Finally, we will create a tour object, and we will write the functions that will initialize the tour, display a tour step, and handle the Previous, Next, and End Tour buttons.

Let's write the code for the aforementioned steps and discuss them in detail.

Initializing accordion and tooltips

The best practice when writing JavaScript is to start by writing a jQuery $(document).ready() handler that ensures the related jQuery or JavaScript code is executed after the page has loaded. Open the tour.js file and start by writing the following code in it:

$(document).ready(function(){
  $('#accordion').accordion({animate : false});
  $(document).tooltip(
  {
    items : '#contact',
    content : function()
    {
      var strContact = '<img src="http://maps.googleapis.com/maps/api/staticmap?center=New+Delhi,India&zoom=13&size=300x200&sensor=false"/>';
      strContact+= '<hr/>In case of any issues, here is the address of our new office in Central Delhi which is well connected to all the places.Feel free to visit us anytime.';
      strContact+= '<hr><span class="ui-icon ui-icon-home" style="float: left; margin-right: 5px;"></span>#23, Rachna Building, Karol Bagh -110005';
      strContact+= '<hr><span class="ui-icon ui-icon-mail-closed" style="float: left; margin-right: 5px;"></span><a href="mailto:awesomecompany@ourlocation.com">awesomecompany@ourlocation.com</a>';
      return strContact;
    }
  });
});

In our markup, we assigned the id value accordion to the div that contains the markup for the accordion. Inside the $(document).ready() handler, the first line initializes the accordion with the animate option set to false. We also want a tooltip to appear on the Contact Us link. To achieve this, we initialize the tooltip component of jQuery UI.

If the tooltip component is initialized without providing any options, it displays the value of the title attribute in a tooltip. Since we want to display custom HTML, we have used two options, items and content, for it. The items option decides which elements will display the tooltip, and in the content option, we can provide any text or HTML to be displayed inside the tooltip. The value of items option has been set to #contact. To set the value of the content option, we created a string. This string contains an image and some information text. The source for image is a static image tile from Google Maps that shows the location of a place. You can set the value from center to any place of your choice.

Now, we can check the progress so far. Save the file and refresh the index.html page in the browser. You will find that jQuery UI's accordion has been styled using jQuery UI's theme and has also become active. Hovering your mouse over the Contact Us link will show the tooltip with an image and the text we defined in the content option while initializing the tooltip.

Initializing accordion and tooltips

Defining the dialog

It's time to initialize the dialog box. We already defined a div with id dialog in the markup. We will convert the said div to jQuery UI dialog box. For this, we need to define settings for the dialog and also the buttons that will appear in it. We will add the following code outside the $(document).ready() event handler to create the dialog:

var tourDialog = $('#dialog').dialog(
{
  minWidth : 315,
  draggable : false,
  buttons: [
  {
    id : 'buttonPrevious',
    text: 'Previous',
    click: function()
    {

    },
    icons:
    {
      primary: 'ui-icon-carat-1-w'
    }
  },
  {
    id : 'buttonNext',
    text: 'Next',
    click: function(event)
    {

    },
    icons:
    {
      secondary: 'ui-icon-carat-1-e'
    }
    },
  {
    text: 'End Tour',
    click: function()
    {

    },
    icons:
    {
      secondary: 'ui-icon-close'
    }

  }
  ],
  show : 'fold',
  hide : 'fold'
});

In the preceding code, we defined five options while creating the dialog. Let's look at all of these one by one:

  • minWidth: This option defines the minimum width the dialog must take. For our purposes, we have set it to 315.
  • draggable: Since the dialog box will appear at specific places denoting respective steps of the tour, we do not want users to drag the dialog. Hence, we have set this option to false.
  • buttons: A dialog box can have one or more buttons that can be defined using the buttons option. We need to provide an array of objects for buttons. Each object in the array represents a button that will be displayed at the bottom of the dialog. We have defined three buttons, each having an id, a display text, a click event handler, and an icon. The first button has the id buttonPrevious and display text is Previous; we have also provided a primary icon for this button. The second button has id buttonNext and the display text is Next. Note that we have provided a secondary icon here. The difference between the primary and secondary icons is that the primary icon appears on the left of the text and the secondary icon appears to the right of the text. The third button has no id but has the display text End Tour and a secondary icon. We have left the click event handlers for all three buttons empty for now. We will go back to these event handlers later in the chapter.
  • show: We can decide which effect will be used when the dialog is displayed. Any value mentioned in jQuery UI effects can be provided.
  • hide: This is used to define the effect that will be used when the dialog closes.

After writing the preceding code, if you reload the index.html page in your browser, you will see that an empty dialog box with three buttons appears at the centre of the page as soon as the page finishes loading.

Defining the dialog

We definitely do not want this to happen; the dialog must be displayed at the appropriate position only when the Start Tour button is clicked. Hence, we will add another option to the dialog, which will initialize the dialog but will not display it automatically. Add another option to the dialog initialization code with the following code:

autoOpen : false

Note

If you have other options defined after the preceding code, make sure that you end the line with a comma. However, there is no need for a comma if you defined autoOpen as the last option.

You can check again by reloading the page; the dialog box will not appear now.

Another important thing to note is that we used the variable tourDialog to keep a reference to the initialized dialog. This tourDialog variable will be used henceforth in the code to access the dialog.

Defining the tour steps

For the home page we have designed, our tour will have 12 steps. Each of these steps will be represented by a JavaScript object that has the following structure:

{
  element : '#logo',
  title : 'We have changed the logo !',
  content : 'Did you notice that we have made some changes to our logo as well. ',
  sequence : 1
}

Let's see what each property does:

  • element: This indicates the HTML element where the tour dialog box will appear
  • title: This is the title that will be displayed in the dialog box
  • content: This is the HTML content displayed in the dialog box
  • sequence: This is a number indicating the step of the tour. We will start with 1 and proceed with 2, 3, and so on

Since we plan to display the tour steps on accordion as well, we will need two more properties:

  • isAccordion: The value for this property will be set to true if the element property is part of an accordion.
  • accordionIndex: This is the 0-based index of accordion panels where the tour step will be displayed. Using this property value, we will be able to open a specific panel of accordion before displaying a tour step.

Here is the full structure of steps of the tour for our example home page in this chapter:

var steps =
  [
    {
      element : '#logo',
      title : 'We have changed the logo !',
      content : 'Did you notice that we have made some changes to our logo as well. ',
      sequence : 1
    },
    {
      element : '#menu',
      title : 'Menu On Left',
      content : 'We have placed all the menu items on left hand side for quick access.',
      sequence : 2
    },
    {
      element : '#orders',
      title : 'Your Orders',
      content : 'Orders menu has a submenu which links to different pages.',
      sequence : 3
    },
    {
      element : '#profile',
      title : 'Your Profile',
      content : 'This link will take you to your profile page where you will be able to edit your profile and change password among other things',
      sequence : 4
    },
    {
      element : '#help',
      title : 'Get Help',
      content : 'Use this link to get help related to any issues',
      sequence : 5
    },
    {
      element : '#lastLink',
      title : 'Last Menu Link',
      content : 'This is last link of menu',
      sequence : 6
    },
    {
      element : '#section1',
      title : 'Buy Cat Posters',
      content : 'We have introduced a new category where you can buy posters of cute cats ',
      isAccordion : true,
      accordionIndex : 0,
      sequence : 7
    },
    {
      element : '#section2',
      title : 'Buy Dog Posters',
      content : 'Dog lovers also welcome.',
      isAccordion : true,
      accordionIndex : 1,
      sequence : 8
    },
    {
      element : '#section3',
      title : 'Watch videos',
      content : 'We have collected some of the best videos from web and you can see them here',
      isAccordion : true,
      accordionIndex : 2,
      sequence : 9
    },
    {
      element : '#cart',
      title : 'Your Cart',
      content : 'This is your shopping cart where all the products you have selected will be displayed.',
      sequence : 10
    },
    {
      element : '#contact',
      title : 'Contact Us',
      content : function()
      {
        var strContact = '<img src="http://maps.googleapis.com/maps/api/staticmap?center=New+Delhi,India&zoom=13&size=280x200&sensor=false"/>';
        strContact+= '<hr/>In case of any issues, here is the address of our new office in Central Delhi which is well connected to all the places.Feel free to visit us anytime.';
        strContact+= '<hr><span class="ui-icon ui-icon-home" style="float: left; margin-right: 5px;"></span>#23, Rachna Building, Karol Bagh -110005';
        strContact+= '<hr><span class="ui-icon ui-icon-mail-closed" style="float: left; margin-right: 5px;"></span>awesomecompany@ourlocation.com';
        strContact+= '<hr>You can take your mouse over Contact Us link if you want to see this information later.';
        return strContact;
      },
      sequence : 11
    },
    {
      element : '#startTour',
      title : 'Thank You!',
      content : 'Thank you for going through through the tour.',
      sequence : 12
    }
  ];

We defined an array named steps with 12 objects. On going through each of these objects, you will see that we are starting with the logo, then proceeding to the menu, and so on. For quick reference, here are the id values of all the HTML elements where tour will be displayed one step at a time:

  • logo
  • menu
  • orders
  • profile
  • help
  • lastLink
  • section1
  • section2
  • section3
  • cart
  • contact
  • startTour

Initializing the tour

We have prepared the entire markup and JavaScript required to build the tour. Let's dive in and write some awesome JavaScript code. But before that, I would like to introduce you to a JavaScript best coding practice.

Since the code size will grow in this chapter, as well as in following chapters, we will use a JavaScript pattern called object literal to organize our code. Simply put, an object literal pattern implies defining a single JavaScript object with a set of comma-separated key-value pairs. Keys can be strings or identifiers and values can be strings, identifiers, or functions. The advantage of this pattern is that it does not pollute the global namespace. Keys that are defined inside the object will not be accessible directly outside that object. You will see this in practice in a moment.

First of all, visit the $(document).ready() event handler and add the following line of code in the end:

tour.init();

The preceding line calls the init function of a JavaScript object named tour. Outside the $(document).ready() event handler, let's define the tour object and its init function with the following code:

var tour =
  {
    triggerElement : '#startTour',
    tourStep : 0, 
    tourSteps : steps,
    defaultTitle : 'Welcome to the tour !',
    defaultContent: 'This tour will show you the new changes we have made to our site layout. <br> Please use next previous buttons to proceed. Click the End Tour button whenever you want to finish the tour.',
    init : function()
    {
      if(this.tourSteps == undefined || this.tourSteps.length == 0)
      {
        alert('Cannot start tour');
        return;
      }
      
      $(this.triggerElement).on('click', function(event)
      {
        tour.showStep(tour.defaultTitle, tour.defaultContent, $(this));
        return false;
      });
    }
  };

The value of the triggerElement property is the id of the element, by clicking on which the tour will start. You can set it to any element. In our example page, we have created an element with the id startTour for this purpose. The tourStep property will keep track of the current step of the tour. Then, the tourSteps property to which we have assigned the variable steps. This variable is the array of twelve steps of the tour that we declared earlier. After this, there are two more properties named, defaultTitle and defaultContent. The defaultTitle property contains some text that will be displayed as the title of the dialog box when the triggerElement is clicked. Similarly, tourContent will serve as the content of dialog box.

The init property declares a closure. Inside this closure, we check to make sure whether the tourSteps variable has been defined and is actually an array. After this, a click event handler is added to the triggerElement. This event handler will be responsible for opening the dialog initially. From here, the user will start navigating the tour using the Previous and Next buttons. The event handler calls the showStep function. The showStep function will be responsible for displaying the dialog box and positioning it correctly next to the element, as defined in the element property of the tourSteps array. Three parameters are passed to the showStep function. Since this is the first time dialog opens, we pass defaultTitle, defaultContent, and the current trigger element startElement. We need to display the dialog box for the first time now. This will all be explained in the next section.

Displaying a tour step

The showStep function is responsible for displaying the dialog box and enabling or disabling the Previous and Next buttons. After the init function, write the following code to define the showStep function and another function named prevNextButtons:

showStep : function(tourStepTitle, tourStepContent, whichElement)
{
  this.prevNextButtons();
  $('body').animate(
  {
    scrollTop: $(whichElement).offset().top
  }, 500, function()
    {
      $('.ui-state-highlight').removeClass('ui-state-highlight');
        
      $(whichElement).addClass('ui-state-highlight');
      
      tourDialog.dialog('option', 'title', tourStepTitle);

      tourDialog.html(tourStepContent);

      tourDialog.dialog('option', 'position', { my: 'left top', at: 'right top', of: whichElement, collision : 'flipfit' });
      
      tourDialog.dialog('open');
    });
},
prevNextButtons : function()
{
  $('#buttonNext').button('enable');
  $('#buttonPrevious').button('enable');
  if(this.tourStep == 0 || this.tourStep == 1)
  {
    $('#buttonPrevious').button('disable');
  }
  if(this.tourStep == this.tourSteps.length)
  {
    $('#buttonNext').button('disable');
  }
  return;
}

Note

If you keep the code for showStep after init, make sure that you have placed a comma (,) after the closing brace of the init closure.

Inside the showStep function, we receive three function arguments: the title text, content HTML, and the target element where a step of the tour will be displayed.

In the first line of showStep, we have called another function called prevNextButtons. Since the showStep function will display and position a tour step, we need to enable or disable the Previous and Next buttons. The prevNextButtons function is used to achieve this. If the user is on the first step of the tour, the Previous button will be disabled; if the user is on the last step of the tour, the Next button will be disabled.

First, we enable both the Next and Previous buttons. Then, we check the tourStep property to determine which step is being displayed currently. If it is the welcome dialog that is shown after clicking on the Start Tour link or the first step of the tour, we disable the Previous button. In the next line, we disable the Next button. If the value of the tourStep variable equals the length of all the steps in the array, it means the user is on the last step of the tour and hence we disable the Next button.

The End Tour button need not be handled, since the user can choose to end the tour during any step.

With the Next and Previous buttons taken care of, the control now returns to the showStep function.

Inside the showStep function, we used jQuery's animate function to scroll the page first so that the target element (whichElement received as a function argument) scrolls to the top of the viewport in 500 milliseconds. After scrolling is done, the callback function for animate fires. We remove the CSS class ui-state-highlight if it has been applied to any element. This is to ensure that there is no highlighted element in the page. In the very next line, the same class is applied to the current element (represented by whichElement) to highlight it. The ui-state-highlight class from jQuery UI's theme framework applies a yellowish background color to an element to make it look highlighted.

We have already initialized the dialog component inside the $(document).ready() event handler earlier. We now set the title of dialog box using jQuery UI's option method. Then, we set its inner HTML.

Now, we are left with one most important thing, that is, positioning the dialog. Remember that target elements can be in any corner of the web page, so we need to position the dialog so that its maximum area is available. To position the dialog, we have used the position option of jQuery UI. To properly position the dialog, we have set four properties of the position option. The name of the first property is my. We have set it to left top. Another property is at, which we have set to right top. Setting it to right top will align the dialog to the right top of the target element. The third property is of, where we provide the element that we want to position the dialog against. Since we want the dialog to be positioned against the target element, we provide it as a value of the of parameter. The last property is collision, which is used to ensure that the maximum part of the dialog is visible. We supplied the value flip. This property checks to either side of the element and positions it to the side where more space is available. With this, our positioning is done. We can now display the dialog.

In the last line of showStep, we called the open method of the jQuery UI dialog which displays the dialog. We can now check the result of our hard work so far in the browser. Reload the index.html page and click on the Take a Tour link. Windows will scroll a little and then the dialog will open with a fold effect. It will have the Previous button disabled.

Displaying a tour step

Making the Previous and Next buttons functional

We have successfully started the tour by showing a welcome dialog. Now, we need to make use of the Previous and Next buttons, and the tourSteps array to navigate them.

If you recall, while we created the dialog earlier in the chapter, we created the Previous and Next buttons and their event handlers. We will now make these buttons functional by calling appropriate code for moving forward or backward in the tour. Visit the code where buttons have been defined, and write the following code inside the click handler for the Previous button:

tour.navigate('previous');

Similarly, go to the click handler for the Next button and write the following code:

tour.navigate('next');

With this done, we will now define the navigate method. The navigate method will be defined inside the tour object after the code for the prevNextButtons function. Though you can define the function anywhere inside the tour object, writing the methods in sequence makes the code more readable:

navigate : function(previousOrNext)
{
  if(previousOrNext == 'previous')
  {
    (this.tourStep) = (this.tourStep) - 1;
  }
  else 
  {
    this.tourStep = this.tourStep + 1;
  }
  
  for(var i = 0; i<this.tourSteps.length; i++)
  {
    if(this.tourSteps[i].sequence == this.tourStep)
    {
      if(this.tourSteps[i].isAccordion)
      {
        $("#accordion" ).accordion("option", "active" , this.tourSteps[i].accordionIndex);
      }
      this.showStep(this.tourSteps[i].title, this.tourSteps[i].content, this.tourSteps[i].element);
      return;
    }
  }
}

A single function will be used for handling both the Previous and Next buttons. The navigate function has an previousOrNext argument, based on which we can decide if the tour has to be moved forward or backward. If its value is previous, we increment the tourStep property of the tour object by one. Otherwise, we decrement its value by one. The increment or decrement in value ensures that we will pick the correct element, title, and content properties from the tourSteps object while moving forward or backward.

A for loop is used to iterate in the tourSteps array. When we find that the sequence value of a tourSteps object matches the tourStep value, we call the showStep function and send the corresponding object's values for element, title, and content. There is another check that we place here for accordion. We have already defined the isAccordion and accordionIndex properties for the tour steps. So, if we find that the isAccordion value is true, we activate the corresponding accordion panel.

We can now check whether the Previous and Next buttons are working by reloading the page in our browser. A typical screen when the tour is on an accordion panel will look like this:

Making the Previous and Next buttons functional

Ending the tour

Ending the tour is very simple compared to the complex code we have written so far. Just visit the event handler for the End Tour button and use the following code:

tour.endTour();

We called the endTour method of the tour object. Let's define it as well. Add the following code inside the tour object:

endTour : function()
{
  this.tourStep = 1;
  $('.ui-state-highlight').removeClass('ui-state-highlight');
  tourDialog.dialog( 'close' );
}

The preceding code simply resets the tourStep to 1 so that the correct data is displayed when the tour is started again. The CSS class ui-state-highlight is also removed from any elements in the page.

Finally, the close method of dialog component is called, which hides the tour dialog with the fold animation.