tags:

views:

65

answers:

2

I have been trying to make this to be a little jQuery plugin that I can reuse in the future without having to write the following into my actions.js file in full.

This works when loaded in the same file where I set the height using my variable tallest.

var tallest = null;
$('.slideshow img').each(function(index) {
  if ($(this).height() >= tallest ) {
    tallest = $(this).height();
  }
});
$('.slideshow').height(tallest);

This works and will cycle through all the items, then set the value of tallest to the greatest height found. The following however does not work:

This would be the plugin, loaded from its own file (before the actions.js file that contains the parts using this):

(function($){
  $.fn.extend({
    tallest: function() {
      var tallest = null;

      return this.each(function() {
        if ($(this).height() >= tallest ) {
          tallest = $(this).height();
        }
      });
    }
  });
})(jQuery);

Once loaded I am trying to use it as follows:

$('.slideshow img').tallest();
$('.slideshow').height(tallest);

However the above two lines return an error of 'tallest is undefined'. How can I make this work? Any ideas would be appreciated. Thinking about this even more the perfect usage of this would be as follows:

$('.container').height(tallest('.container item'));

But I wouldn't even know where to begin to get this to work in the manner that you pass the object to be measured into the function by adding it into the brackets of the function name.

Thanks for reading,

Jannis

+4  A: 

The reason you're getting 'tallest is undefined' is because tallest only exists inside the scope in which it is defined and can't be accessed from outside. You need to encapsulate all your logic inside the plugin. Here is how you might do it:

(function($) {
  $.fn.tallest = function(itemsSelector) {
    return this.each(function() {
      var $this = $(this),
        $items = $(itemsSelector, this),
        tallest = 0;

      $items.each(function() {
        var $thisItem = $(this);
        if ($thisItem.height() > tallest) {
          tallest = $thisItem.height();
        }
      });

      $this.height(tallest);
    });
  };
})(jQuery);

// used like this:
$('.container').tallest('.item');

Edit:

Starting a variable name with a $ is a convention inspired by Hungarian Notation and is used to indicate that the variable contains a jQuery object. It's not required to name your variables this way, but I find it useful.

I also use var $this = $(this); here to store a jQuery object that wraps the DOM object currently being operated on. The reason for doing this is for performance - every time you need to reference the current object in the loop, you can use $this and won't be creating an identical new object in memory. In this example the benefit is not very clear because $this is only used once anyway. This technique is also used inside the inner loop by assigning the current object to $thisItem. Since this object is referred to twice in the loop, you save an object instantiation.

Jimmy Cuadra
Thanks, this works great and I have learned quite a bit about how to build these type of things! The one line I cannot explain though is `var $this = $(this)` to what purpose is this being done? Also: Why do you use `var $var_name = ...` not `var var_name = ...` does it make a difference? Thanks again.
Jannis
I edited my answer to explain those things. :)
Jimmy Cuadra
Awesome, thanks for the detailed explanation. Really appreciate it!
Jannis
+2  A: 

Try this shorter (and more generalized) version of what you are trying to do:

$.fn.extend({
  max: function(prop) {
    // create an array of all property values
    var props = $.map(this, function(i) { return i[prop]; });
    // return the result of Math.max() applied to this array
    return Math.max.apply(Math, props);
  }
});

Use it like this:

var tallest = $(".slideshow img").max("height");
var widest  = $(".slideshow img").max("width");

If you use several JavaScript libraries in parallel you might want to ensure that $ means jQuery within the plugin. The usual way to do this is to wrap the above in an anonymous function, like this:

(function($) {
  $.fn.extend({
    // ...
  });
)(jQuery);
Tomalak
that's a nice approach, but jQuery's `height` function overrides the height property of the image. So you'd have to make a change to the way that property is retrieved, `i[prop]()`, `i.attr(prop)`, or `this[prop]`, or something else.
Anurag
@Anurak: this is irrlevant since `i` will never be a jQuery object. It will always be a DOM object.
Tomalak
@Tomalak.. you're right. I got tunnel visioned. `i` will just be a DOM object.
Anurag
thanks for the answer. looks to be much more complex and more useful when a wider range of attributes are needed. in this case the above solution works great but I can see the possible benefits yours might have. I will note it down for future use. Thanks.
Jannis
@Jannis: I think we have different interpretations of the word "complex".
Tomalak
It's probably to do with my scope of jQuery simply being rather narrow. For instance while I can somewhat read the function you're performing I don't know how to get from 'var tallest = ..' to applying the highest value to my '.container'.I think there is still a lot of things jQuery I need to learn
Jannis
@Jannis: Would `$(".container").height($(".container item").max("height"));` not work?
Tomalak