Reputation: 7543
I'm trying to develop my first jQuery plugin. Basically it appends classes to various elements on site an then changes it when user scrolls (I'm calculating offsets and whatnot). And I think I've hit a wall with this one.
Here's how I start the plugin:
$("div").myPlugin();
And the source:
$.fn.myPlugin = function() {
return this.each(function() {
$(this).addClass("something-" + i);
i++;
});
};
Could someone explain please how to use $(window).scroll in my plugin?
Because once it into "return this.each" it gets attached as many times as many elements there are in this loop...
$.fn.myPlugin = function() {
return this.each(function() {
$(this).addClass("something-" + i);
i++;
$(window).scroll( function() { <!-- here it not only attaches itself x-times but i is always the same -->
...
$(".something-" + i).addClass("active");
...
});
});
};
Putting it after return doesn't do nothing:
$.fn.myPlugin = function() {
return this.each(function() {
$(this).addClass("something-" + i);
i++;
});
$(window).scroll( function() { <!-- doesn't seem to work -->
...
$(".something-" + i).addClass("active");
...
});
};
And before there are no "i"s, also I don't know if I can put anything outside of the "return" scope inside a function (doesn't seem valid to me?):
$.fn.myPlugin = function() {
$(window).scroll( function() { <!-- there is no "i" yet -->
...
$(".something-" + i).addClass("active");
...
});
return this.each(function() {
$(this).addClass("something-" + i);
i++;
});
};
I'm new to jQuery and I'm not sure if I understand the documentation correctly, I was wondering wether it might be better to do not use return here at all? Note this plugin can work with 1 element but usually there will be more divs than 1.
Thanks a lot.
Upvotes: 1
Views: 728
Reputation: 19288
There are probably several ways to write the plugin. Here are the characteristics of what seems most appropriate :
this
- no need for .each()
(at least, not overtly).Please note that these characteristics, when coded, will be in a very atypical plugin pattern. Therefore, this is probably not the best problem to choose for your first plugin.
However, here goes ...
(function($, window, undefined) {
// Private vars
var pluginName = 'myPlugin',
jqCollection = $(),
events = {
scroll: 'scroll.' + pluginName
},
className = pluginName + 'active',
timeout;
// Private functions
function scroll() {
if(jqCollection.closest('body').length === 0) {
//This is advisable in case some other javascript/DOM activity has removed all elements in jqCollection.
// Thanks to selected answer at :
// http://stackoverflow.com/questions/4040715/check-if-cached-jquery-object-is-still-in-dom
jqCollection = $();
$(window).off(events.scroll);
} else {
jqCollection.addClass(className);
clearTimeout(timeout);
timeout = setTimeout(function() {
jqCollection.removeClass(className);
}, 100);
}
}
// Public methods
methods = {
add: function(jq) {
jqCollection = jqCollection.add(jq);
if(jqCollection.length > 0) {
$(window).off(events.scroll).on(events.scroll, scroll)
}
},
remove: function(jq) {
jqCollection = jqCollection.not(jq);
if(jqCollection.length === 0) {
$(window).off(events.scroll);
}
}
};
// The actual plugin
$.fn[pluginName] = function(method) {
method = method || 'add';
if(methods[method]) {
methods[method](this);
} else {
console.log('jQuery plugin ' + pluginName + 'has no method: ' + method);
};
return this;
};
})(jQuery, window);
Associate selected elements as follows :
$(selector).myPlugin();
//or
$(selector).myPlugin('add');
Dissociate selected elements as follows :
$(selector).myPlugin('remove');
Note that I changed the class name to pluginName + 'active'
to make it far less likely to be used by some other piece of code.
As I said, that's one way to do it. Maybe you can think of improvements or some other way entirely.
Upvotes: 0
Reputation: 1556
How about this:
(function($, window, undefined){
$.fn.myPlugin = function() {
// bind the scroll event listener once
$(window).scroll(function(){
console.log('scroll');
});
// iterate your plugin elements
// use index passed by the .each callback:
this.each(function(i, el){
$(el).addClass('something-' + i);
});
// return your jQuery object to allow chaining on your plugin
// note that (this instanceof jQuery) === true, so there is no need
// to pass it to the jQuery function i.e. $(this)
return this;
};
})(jQuery, window);
$('div').myPlugin();
console.log($('div').map(function(){ return this.className; }).get());
http://jsfiddle.net/yw0q5hn7/2/
Upvotes: 2