I'm working on a jQuery plugin that can be applied to multiple elements. The plugin includes some animation effects and I need to manage the event queue based on if the plugin is used on multiple elements (instead of one).
What's the best way to detect if the plugin has been applied to a single element or to multiple elements?
Edit...
The length property works correctly if the plugin is passed multiple elements (such as $('.myClass').plugin()) but if the plugin is called on multiple, single elements (such as $('#myElem1').plugin() and $('#myElem2').plugin()) then the length returns one for each call.
Is there are a way to detect multiple instances when the plugin is used as in the second example/
this inside the plugin, depending on your style, refers to the jQuery object, so you can just check the .length property, for example:
jQuery.fn.plugin = function(options) {
if(this.length > 1) //being applied to multiple elements
};
For your edit: tracking the total may be a better option, for example:
(function($) {
$.pluginCount = 0;
$.fn.plugin = function(options) {
$.pluginCount += this.length; //add to total
if($.pluginCount > 1) //overall being applied to multiple elements
};
})(jQuery)
Assuming you apply your plugin by $('some selector').myPlugin(), the "this" keyword will refer to the jquery object you've called the plugin on inside your plugin function.
So, to summarize:
(function( $ ){
$.fn.myPlugin = function() {
if(this.size() > 1) {
//code for multiple elements
}
else if(this.size() == 1) {
//code for 1 element
}
}
})( jQuery );
$('div.to-pluginize').myPlugin();
If you want a generic way to test whether or not a plugin has been applied to a arbitrary set of elements, here is one approach:
// say we want to apply this to a bunch of elements
$.fn.test = function(str) {
this.each(function() {
// set a boolean data property for each element to which the plugin is applied
$(this).data('test', true);
alert(str);
});
};
// use a simple plugin to extract the count of a specified plugin
// from a set of elements
$.fn.pluginApplied = function(pluginName) {
var count = 0;
this.each(function() {
if($(this).data(pluginName)) {
count++;
}
});
return count;
};
with this markup:
Hello
<br />
Hello
Here is a test:
$("a").test('test');
alert($("*").pluginApplied('test')); // alerts '2'
Demo: http://jsfiddle.net/B5QVC/
Related
I want to apply the same script on multiple pages, but I need to store some var inside, which may not be present on particular pages.
window.onorientationchange = function () {
var $t1 = $(".test1")[0];
var $t2 = $(".test2")[0];
var $t3 = $(".test3")[0];
var $t4 = $(".test4")[0];
var $t5 = $(".test5")[0];
// do some stuff
}
I want to store this code in .js file and then apply it across several pages, the problem is that some of this var's are not present on particular pages, how do I make it universal?
Also
If I add lines like:
if (window.matchMedia("(orientation: portrait)").matches) {
if ($t1.is(":empty") && $t2.is(":visible")) {}}
inside mentioned event listener, how do I deal with an "empty" var's, which is not defined on the previous step?
Several things.
Based on your variable naming, it looks like you are expecting $t1 to be a jQuery object.
However, when you try to access an element by index [0], you are returning the first element that matched the selector, no longer wrapped as a jQuery object.
What you want is to use the .eq(0) function to access the element by index, so a jQuery object is returned
https://api.jquery.com/eq/
var $t1 = $(".test1").eq(0);
At that point, you can use the .length test to check if your $t1 contains any elements
window.onorientationchange = function () {
var $t1 = $(".test1").eq(0);
// ...
if($t1.length){
// do stuff with $t1
}
}
Let's say I got a DOM element, as a param of an event, for example click.
$(document).click(function() {
myElement = $(this);
});
How can I check later that myElement is still in the DOM?
I can't use .length or any other things like that because it still refer to the saved element and the state of the DOM at this moment, right?
You can check element parent:
function isInDom(obj) {
var root = obj.parents('html')[0]
return !!(root && root === document.documentElement);
}
if(isInDom(myElement)) {
...
}
Here's working fiddle: http://jsfiddle.net/vnxhQ/7/
You're probably looking for Node.isConnected.
The only reliable way I see so far is to check if the element is inside document.getElementsByTagName('*')
function isInDoc(element)
{
var elements=document.getElementsByTagName(element.tagName);
for(var i=0;i<elements.length;++i)
{
if(elements[i]===element)return true;
}
return false;
}
Demo: http://jsfiddle.net/doktormolle/hX8eN/
<edit>
Node.contains() seems to be supported by all major browsers, so I would finally suggest this:
if(document.documentElement.contains(myElement))
Demo: http://jsfiddle.net/doktormolle/LZUx3/
In order to test if an element still exists in the DOM, you need to crawl the DOM again:
// store it in some shared scope
var myElement;
$(document).click(function() {
myElement = $(this);
});
// sometime later...
if ($('#' + myElement.attr('id')).length > 0) {
// it still exists
} else {
// it no longer exists
}
Your clicked elements must all have ids for this to work, though. A class or any other selector could be used instead.
Edit: see this question for ideas on how to get a unique selector for any given DOM element.
This is assuming you would have the id of possible 'clickable' elements set
var idRef;
$(document).on('click', function() {
idRef = this.id;
});
later..
var exists = document.getElementById(idRef).length > 0;
You can the undocumented .selector property of a jQuery object to see if an element is still in the DOM, on the condition that it uses a unique ID. Obvious
http://jsfiddle.net/mblase75/CC2Vn/
$two = $('#two');
console.log($($two.selector).length); // 1
$two.remove();
console.log($($two.selector).length); // 0
See this question for more about how to get the selector of a jQuery object, which may or may not uniquely describe the DOM element(s) it contains.
Just to add something to the fray:
This is really Inferpse's answer slightly tweaked for Dr.Molle's corner case of creating another body element that might house the element removed from the general DOM tree (or, of course, maybe the element was never in the DOM in the first place.) Like Inferspe's answer, it takes a jQuery wrapped object, not the element itself.
function isInDom(jqobj) {
var someBody = jqobj.parents('body');
return someBody.length > 0 && someBody.get(0) === document.body;
}
I must admit I'm having trouble figuring out how I might try to break that.
Edit: Oh yeah... jsFiddle: http://jsfiddle.net/vnxhQ/5/
Edit II: of course, if we aren't talking about link or script elements (anything that might go into the head, not the body, I guess that might work fine :-/
Perhaps a better way of implementing Inferpse's function is by extending jQuery:
jQuery.fn.extend({
isInDom: function() {
var root = this.eq(0).parents('html')[0];
return !!(root && root === document.documentElement);
}
})
Usage:
$("div").isInDom() //returns true if your dom contains a div
$("<div />").isInDom() //returns false
$().isInDom() //returns false
What is the equivalent code in jQuery for the below JavaScript function?
function attachSomeHandler(in) {
for(var i = 0; i < in.length; i++) {
if(in[i].type == 'submit')
in[i].attachEvent("onclick", someFunc);
}
}
Assuming the argument in is an array of DOM elements and you already have that array and you want to reproduce the exact same function attachSomeHandler(), you could do this using jQuery:
function attachSomeHandler(in) {
$(in).filter('[type="submit"]').click(someFunc);
}
If you want to back up to a higher level in your code, you can create a single jQuery selector that would get the original array of DOM elements that was already filtered to only have ones with the appropriate type.
Yes, there are many topics about that, but I still didn't get it.
I prepared two jsfiddle:
return this
return this.each()
What's the difference?
There any many answers, but my examples show the same output. So some some of these answers might be wrong!?
what does "return this.each()" do in jQuery?
"It allows for one to call a plugin or an event on a bunch of elements and then apply that same function or event to all of them" --> work's with return this as well
"It allows you to chain multiple functions" --> same here
"Allows you to do things like: $("mySelector").foo().show();" --> I still can do this as well, when I use return this
I also created another jsfiddle that shows - in my opinion - that it doesn't matter if you're wrapping you code into return this.each();:
http://jsfiddle.net/7S3MW/1/
The Chrome Console shows the same output!
So what's the difference?
Two things:
Your examples are flawed in that they do exactly the same thing to each element.
The real issue isn't return this versus return this.each, the issue is this versus this.each.
For (1), consider the difference between this plugin:
(function($) {
$.fn.mangle = function(options) {
this.append(' - ' + this.data('x'));
return this;
};
})(jQuery);
Demo: http://jsfiddle.net/ambiguous/eyHeu/
And this plugin:
(function($) {
$.fn.mangle = function(options) {
return this.each(function() {
$(this).append(' - ' + $(this).data('x'));
});
};
})(jQuery);
Demo: http://jsfiddle.net/ambiguous/5dMMH/
So you see, you need to use this.each if you need to treat the individual elements in the this set differently. You would have similar effects if your plugin had to attach element-specific data to each element: if you didn't use each then you'd end up attaching the exact same piece of data to all of the elements inside this and that would just leave you confused about why information is bleeding all over the place.
For (2), it doesn't matter if you return this or return this.each(... since x.each(...) returns x anyway.
Let me show you two "equivalent" pieces of code that could clarify your question:
With jQuery "each" function:
(function($) {
$.fn.mangle = function(options) {
return this.each(function() {
$(this).append(' - ' + $(this).data('x'));
});
};
})(jQuery);
Without jQuery "each" function:
(function($) {
$.fn.mangle = function(options) {
var objs = this;
for (var i=0; i<objs.length; i++) {
var obj = objs[i];
$(obj).append(' - ' + $(obj).data('x'));
};
return this;
};
})(jQuery);
So, basically, each function is used to apply some code to all elements contained in this object (as this usually refers to a group of elements returned by a jQuery selector) and return the reference to this (as each function always returns that reference -to allow chaining calls-)
As a side note: The second approach (-for loop-) is faster (notably on old browsers) than former one (-each function-).
They are both exactly the same. .each() returns this, so return this.each() is EXACTLY the same as this.each(); return this;
Edit: your newest fiddle's makeRed method doesn't return this and is therefore not chainable.
I am trying to add a function named rows to the jqGrid jQuery plugin, but I can't determine the syntax. Here are my non-working versions.
(function($) {
$.fn.jgrid.rows = function(data) {
// do something
};
});
(function($) {
$.fn.rows = function(data) {
// do something
};
});
$.jqgrid.fn.rows = function(data) {
// do something
};
$.fn.rows = function(data) {
// do something
};
What would be the proper syntax?
Thanks!
It seems the correct answer on your question depends a little from what should do the method rows which you want to implement. I try to guess a little and gives the implementation which correspond to my understanding of your question.
First of all jqGrid is jQuery plugin and if you write for example
$(myselector).jqGrid('setSelection',rowid);
it can be that $(myselector) selects more as one DOM element. For example
$('table').jqGrid('setSelection',rowid);
will try call jqGrid method 'setSelection' on all <table> elements on the page. So this element in the array of DOM elements (it should be <table> DOM elements) and not only one element.
Another general remark. There are jQuery methods which can be chained like
$("#list").jqGrid('setGridParam',{datatype:'json'}).trigger('reloadGrid');
In the case the 'setGridParam' do something and return this to support chaining. Other methods don't support chaining and return what the method need to return. For example getDataIDs returns the array of ids and one can't chain getDataIDs with another jQuery methods.
Now I return back to your question. I would better name the new method getRowsById. The method will return array with DOM elements which represent <tr> (table row). The method will have rowid as the parameter. Then one can extend jqGrid with the new method in the way:
$.jgrid.extend({
getRowsById: function (rowid){
var totalRows = [];
// enum all elements of the jQuery object
this.each(function(){
if (!this.grid) { return; }
// this is the DOM of the table
// we
var tr = this.rows.namedItem(rowid);
if (tr !== null) { // or if (tr !== null)
totalRows.push(tr);
}
});
return totalRows;
}
});
First of all I use in the example the method $.jgrid.extend defined here. It does mostly $.extend($.fn.jqGrid,methods);. Then, because the method which we implement can't be chained, we define totalRows variable which will be returned later as the result of the method. Now we have to enumerate all objects from the this (like elements of $(myselector) or $('table') in the examples above). We do this with respect of this.each(function(){/*do here*/}); construct. Then inside of the loop we do following
if (!this.grid) { return; }
With the statement we test whether the current DOM element has grid property. It is not a standard property of the table element, but jqGrid extend the DOM elements of the table with the property. With the test we could skip for example other table elements where the jqGrid are not applied (which are not a jqGrid). Then I use the fact that this must be DOM of the table element which has rows property (see here, and here) and I use its namedItem method. The native implemented method works better as $("#"+rowid), but do the same. After all we return the array totalRows. It will have no element if the row with the row id not in the grid and 1 if it is exist. If the current jQuery selector select more as one grid and we had an error and included in both grids rows with the same id the returned array will has length greater as 1. So we can use it so
var grid = $("#list");
var tr = grid.jqGrid('getRowById','1111');
alert(tr.length);
At the end I want to mention that the method $.jgrid.extend can be helpful not only if you want to introduce new jqGrid method. Sometime there are already some jqGrid method, but it does not exactly what you need. So you want that the modified method do something at the beginning or something at the end of the original jqGrid method. In the case we can do the following
var oldEditCell = $.fn.jqGrid.editCell;
$.jgrid.extend({
editCell: function (iRow,iCol, ed){
var ret;
// do someting before
ret = oldEditCell.call (this, iRow, iCol, ed);
// do something after
return ret; // return original or modified results
}
});
In the example we overwrite the original editCell method with will be called by jqGrid itself and do something before of something after the call.
try:
$.extend($.jgrid,{
rows: function() {
// do something
}
});