2
\$\begingroup\$

I'm using grumble to create an interactive tour for a website I'm working on (I'm aware there is a plugin called crumble that does this but I want to have full control of the functionality).

Basically, I initialise 6 grumbles (this is the amount of tour bubbles I need), then hide the last 5 and then attach each click handler separately. In each click handler I hide the one that is clicked and show the next one.

$(document).ready(function() {

 // First grumble
 $('#someID').grumble({
  text: 'This is a short tour, click to continue', 
  angle: 130, 
  distance: 10,
  type: 'firstGrumble '
 });

 // Second grumble
 $('#someOtherID').grumble({
  text: 'This is some tour text',
  angle: 30, 
  distance: 230,
  type: 'secondGrumble '
 });

 // Third grumble
  $('#someOtherOtherID').grumble({
  text: 'This is some more tour text',
  angle: 5, 
  distance: 30,
  type: 'thirdGrumble '
 });

 // Fourth grumble
 $('#anotherID').grumble({
  text: 'This is another bit of tour text',
  angle: 60, 
  distance: 30,
  type: 'fourthGrumble '
 });

 // Fifth grumble
 $('#yetAnotherID').grumble({
  text: 'This is also another bit of tour text',
  distance: 30,
  type: 'fifthGrumble '
 });

 // Sixth grumble
 $('#andAnotherID').grumble({
  text: 'This is the end of the tour',
  angle: 300, 
  distance: 30,
  type: 'sixthGrumble '
 });

 // Hide the freshly created gumbles
 $('.secondGrumble').hide();
 $('.secondGrumble + div').hide();
 $('.thirdGrumble').hide();
 $('.thirdGrumble + div').hide();
 $('.fourthGrumble').hide();
 $('.fourthGrumble + div').hide();
 $('.fifthGrumble').hide();
 $('.fifthGrumble + div').hide();
 $('.sixthGrumble').hide();
 $('.sixthGrumble + div').hide();

 // Click handler for first grumble
 $(document).on("click", '.firstGrumble + div', function() {
  $('.firstGrumble').hide();
  $('.firstGrumble + div').hide();
  $('.secondGrumble').show();
  $('.secondGrumble + div').show();
 });

 // Click handler for second grumble
 $(document).on("click", '.secondGrumble + div', function() {
  $('.secondGrumble').hide();
  $('.secondGrumble + div').hide();
  $('.thirdGrumble').show();
  $('.thirdGrumble + div').show();
 });

 // Click handler for third grumble
 $(document).on("click", '.thirdGrumble + div', function() {
  $('.thirdGrumble').hide();
  $('.thirdGrumble + div').hide();
  $('.fourthGrumble').show();
  $('.fourthGrumble + div').show();
 });

 // Click handler for fourth grumble
 $(document).on("click", '.fourthGrumble + div', function() {
  $('.fourthGrumble').hide();
  $('.fourthGrumble + div').hide();
  $('.fifthGrumble').show();
  $('.fifthGrumble + div').show();
 });

 // Click handler for fifth grumble
 $(document).on("click", '.fifthGrumble + div', function() {
  $('.fifthGrumble').hide();
  $('.fifthGrumble + div').hide();
  $('.sixthGrumble').show();
  $('.sixthGrumble + div').show();
 });

 // Click handler for sixth grumble
 $(document).on("click", '.sixthGrumble + div', function() {
  $('.sixthGrumble').hide();
  $('.sixthGrumble + div').hide();
 });

});

I want to know a way to make this code more DRY. I'm thinking some form of an each statement and a counter to create the 6 classes, but I could be wrong.

\$\endgroup\$
1
  • 3
    \$\begingroup\$ I strongly doubt that #someOtherId is the actual id you use? It will be easier for us to give advice, if you use the actual id's and classes in your code. Welcome to Code Review! \$\endgroup\$
    – Vogel612
    Jul 25, 2014 at 10:28

2 Answers 2

3
\$\begingroup\$

I would go for an approach as follows:

<div id="grumble-1" data-grumble-text="my text" data-grumble-angle="60" data-grumble-distance="30" data-grumble-next="grumble-2">clickme</div>

<div id="grumble-2" data-grumble-text="my text 2" data-grumble-angle="30" data-grumble-distance="10">clickme</div>

then some javascript fun:

var GrumblingSteps = (function(){
    var grumbleMagic = function($grumbleTarget) {
        //create grumble
        $grumbleTarget.grumble({
            text : $grumbleTarget.data('grumble-text'),
            angle : $grumbleTarget.data('grumble-angle'),
            distance : $grumbleTarget.data('grumble-distance')
        });
        //add click handler to the next in line if any
        if ( $grumbleTarget.data('grumble-next') ) {
            $nextGrumble = $('#'+$grumbleTarget.data('grumble-next'));
            $nextGrumble.on('click', function() {
                $grumbleTarget.grumble('hide');
                grumbleMagic($(this));
            });
        }
    }

    return function(firstGrumble) {
        var $firstGrumble = $(firstGrumble);
        grumbleMagic($firstGrumble);
    }
})();

Someone might be able to change this to a jQuery plugin or so. But for now you use it as follows:

GrumblingSteps('#grumble-1');

It might have some errors in it since I coded this directly inside this comment box ;)

\$\endgroup\$
2
  • \$\begingroup\$ Nice solution so far, but I owuld make use of delegating the click events to a common parent of the crumbles not binding to every single grumble. \$\endgroup\$ Jul 25, 2014 at 14:31
  • \$\begingroup\$ @ThomasJunk would be better yes, catch it in a parrent \$\endgroup\$
    – Pinoniq
    Jul 25, 2014 at 23:23
1
\$\begingroup\$

One idea:

var grumbles = [
// id, text, angle, distance, type, hidenByDefault, isLast
['#someID', 'This is a short tour, click to continue', 130, 10, 'firstGrumble', false, false],
['#someOtherID', 'This is some tour text', 30, 230, 'secondGrumble', true, false],
...
];

$(document).ready(function() {
var grumbleLength = grumbles.length;
for (var i = 0; i < grumbleLength; i++) {
 var currentGumble = grumbles[i];

 // create grumbles
 $(grumbles[0]).grumble({
  text: currentGumble[1], 
  angle: currentGumble[2], 
  distance: currentGumble[3],
  type: currentGumble[4]
 }); 

 // hide grumbles
if (grumbles[5]) {
 $('.' + currentGumble[4]).hide();
 $('.' +  currentGumble[4] + ' + div').hide();
}

 // Click handler
 var nextGumble = grumbles[i + 1];
 $(document).on("click", '.' +  currentGumble[4] + ' + div', function() {
  $('.' + currentGumble[4]).hide();
  $('.' +  currentGumble[4] + ' + div').hide();
  if (currentGumble[6]) {
    $('.' + nextGumble[4]).show();
    $('.' +  nextGumble[4] + ' + div').show();
  }
 });       
}
}

This would definitely make it easier to add more gumbles in the future.

Instead of an array for the gumbles, you could also use classes, which would make the code a bit less confusing (currentGrumble.getText() looks a lot nicer than currentGrumble[1]. It would also allow for currentGumble.getTypeDiv() which would make the code nicer as well).

The array/class could also hold an id for the next gumble, so the action in the click handler do not rely on the order of the array.

\$\endgroup\$
1
  • \$\begingroup\$ nice approach. Why you set separate space for "hidden b default" ? Assuming you get your data from json response (seems that way), your first set of data is visible always, all the rest are not. Clicking, hide current visible text and show next set of data \$\endgroup\$
    – andrew
    Jul 25, 2014 at 13:50

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