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
}
});
Related
I'm trying to create some helper functions like what jQuery does but in vanilla JS.
This is what I have;
var $ = function(selector){
var elements = typeof selector == 'string' ? document.querySelectorAll(selector) : selector;
elements.each = function(){
this.forEach(function(item, index){
var element = _(this[index]);
//How do I apply the passed in function to this element??
})
};
}
It is used as such;
var x = 0;
$('.field').each(function(e){
e.addClass('item-' + x);
x++;
});
How do I use the function I am passing in above into the $ object and apply it to the passed in element?
There are A LOT of questions asked around this but not for my specific issue, and I couldn't modify them to suit my situation. Any help is appreciated.
Although epascarello posted a way to lay out the chaining you want to accomplish, I wanna also bring up that the current way you have things set up are contradictory.
One being that you define an each() method on the elements variable you create, and two being that you define the each() method to take no parameters, but then you go on to pass a function as a parameter when you're using the method. Also the way you are referencing the elements you queried is somewhat messed up.
I think you should instead restructure your code into something like:
const $ = function(selector){
let elements;
let bundle = {
get(selector){
return typeof selector == 'string' ? document.querySelectorAll(selector) : selector;
},
each(executor) {
// instead of calling forEach on 'this', call it on the elements instead
// thanks to closures, this each() method still has access to the elements that were queried
elements.forEach(function(item, index){
let tempEle = item;
// you can call the passed executor function here
executor();
});
}
}
elements = bundle.get(selector);
return bundle;
}
So you could then call it as below, where you send in an executor function to be applied each iteration thru the forEach() loop defined within each()
$('.field').each(() => {
console.log("Calling in the each() method");
});
I'm trying to obtain some insight in the context of a jQuery object. I've read a ton of questions on SO, but they all want to fix a specific problem, and the answers thus fix the problem, and didn't really provide me with the knowledge I'm hoping for.
So I have two buttons, and I have a click event that listens for both of them like so:
$('#copyRI, #copyIR').click(function () {...}
Now when I try to establish which element was clicked like so:
$('#copyRI, #copyIR').click(function () {
var test = $(this);
var test2 = $('#copyIR');
var compare = (test === test2);
}
compare returns false when I click the button with id = #copyIR
when debugging I noticed that the context of test and test2 are different:
Now I'm not looking for a way to successfully establish which button was clicked, but I want to obtain some insight in the concept of the "context" in a jQuery object.
Thanks in advance!
When you call $(this) you create a new jQuery object, instantiating it with an HTML Element.
When you call $('#copyIR') you create a new jQuery object, instantiating it with a selector. This stores extra information in the object, including the selector itself.
Even if that wasn't the case, you would be creating two different objects and === (and == for that matter) test if two objects are the same object not if they are identical objects.
$(this) === $(this) would also return false.
If you want to test if the elements are the same, then you can compare the underlying DOM nodes (since those will be the same object)
var compare = (test[0] === test2[0]);
Alternatively, you can just test if the object you have in the first place matches the selector:
var compare = test.is('#copyIR');
You should rather use .is() method here.
Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
CODE:
$('#copyRI, #copyIR').click(function () {
var test = $(this);
var compare = test.is('#copyIR')
}
jQuery contexts (being an Object) are compared by reference, so test === test2 would obviously return false since each variable is pointing to a different jQuery context (the fact that both contexts internally contains a reference to same DOM object doesn't matter).
Try is() instead:
var compare = $(this).is('#copyIR');
See Documetnation
You could simply compare the id.
$('#copyRI, #copyIR').click(function () {
if (this.id === 'copyIR') {
// do something
}
}
I'm building a phonegap project using jQuery mobile.
I have a javascript object that I'm iterating through.
Currently the problem is this:
Below is a method in my model object. It is self recursing, and once called, will recurse through itself to the next level every time a user clicks on a list item generated by the previous level of the object.
What I am battling with is passing the iterated segment, b, into the method itself as an object. For some reason this is returned as a string called [Object], and not the object itself.
This function does work as it's displaying the first level, but something about the "firstString" string I am creating for each child seems to be turning my object into a string named object. I have removed the quotes, placed the object in braces, to no avail.
Would anyone have any idea why this is happening, I'm obviously missing something important regarding passing objects into methods whose call is generated as a string...
My code is below, and line causing the issue is firstString+="model.recurseAppTree('"+b+"');";
recurseAppTree: function(AppTree)
{
$.each(AppTree, function(a,b)
{
var firstString='<li data-role="list-divider" role="heading" data-theme="b">'+b.DisplayValue+'</li>';
if(b.Children != null)
{
$.each(b.Children, function(c,d)
{
firstString+="<li data-theme='c'><a data-transition='slide' id='id-"+d.IdValue+"' href='javascript:void(0);'>"+d.DisplayValue+"</a></li>";
firstString+="<script>";
firstString+="$('#id-"+d.IdValue+"').click(function(){";
firstString+="model.recurseAppTree('"+b+"');";
firstString+="});";
firstString+="</script>";
});
}
$("#selectview").html(firstString);
$("#selectview").listview('refresh', true);
});
},
It's just normal.
You use an object in a string context by the concatenation with +. This tells JS to implicitely cast the object to a string.
b = {}
alert(typeof b) // object
alert(typeof (''+b)) // string
You should use event delegation for your gui
1- Add a (common) class to your '' tags, e.g. unrollLink :
var firstString='<li ...><a class="unrollLink" ...></a></li>"
2- Choose a node in your html, which is a parent of all your "tree" nodes, and will always be present in your html. Delegate the click handler to this node :
$('#selectview').on('click', '.unrollLink', function(){
//this === clicked link - write a function which returns the node you want based on the "id" you set
var myNode = getNode( this.id );
model.recurseAppTree( myNode );
});
3- change your function to produce the adequate html. You don't need to add code for the click events :
recurseAppTree: function(AppTree)
{
$.each(AppTree, function(a,b)
{
var firstString='<li data-role="list-divider" role="heading" data-theme="b">'+b.DisplayValue+'</li>';
if(b.Children != null)
{
$.each(b.Children, function(c,d)
{
// add the class you chose to the clickable items :
firstString+='<li data-theme="c"><a class="unrollLink" data-transition="slide" id="id-'+d.IdValue+'" href="javascript:void(0);">'+d.DisplayValue+'</a></li>';
});
}
$("#selectview").html(firstString);
$("#selectview").listview('refresh', true);
});
},
var brands = document.getElementsByName("brand");
for(var brand in brands){
$("input[name='brand']").eq(brand).click(function(){
alert("hello22");
loadDataFN(1);
});
}
This code is not executing in ie6,
Any help would be appreciated.
The problem is likely that you are trying to use a for-in construct to iterate over a numeric array. This often won't give expected results. Use an incremental for loop instead:
var brands = document.getElementsByName("brand");
// Use an incremental for loop to iterate an array
for(var i=0; i<brands.length; i++){
$("input[name='brand']").eq(brands[i]).click(function(){
alert("hello22");
loadDataFN(1);
});
}
However,
after seeing the first part of your code, the loop appears unnecessary. You should only need the following, since you are assigning the same function to all brand inputs.
// These will return the same list of elements (as long as you don't have non-input elements named brand)
// though the jQuery version will return them as jQuery objects
// rather than plain DOM nodes
var brands = document.getElementsByName("brand");
$("input[name='brand']");
Therefore, the getElementsByName() and loop are not necessary.
$("input[name='brand']").click(function() {
alert("hello22");
loadDataFN(1);
});
for-in loops are used for iterating over the properties of an object, not over the elements of an array.
Why don't you write the code without jQuery if this doesn't work?
Something like this:
function getInputByName(name) {
var i, j = document.getElementsByTagName('input').length;
for(i=0;i<j;++i) { // You can also use getAttribute, but maybe it won't work in IE6
if(document.getElementsByTagName('input')[i].name === name) {
return document.getElementsByTagName('input')[i];
}
}
return null;
}
I don't know jQuery, but maybe you can do something like this:
$(getInputByName('brand')).eq(brand).click(function(){
alert("hello22");
loadDataFN(1);
});
I have this JavaScript code:
for (var idx in data) {
var row = $("<tr></tr>");
row.click(function() { alert(idx); });
table.append(row);
}
So I'm looking through an array, dynamically creating rows (the part where I create the cells is omitted as it's not important). Important is that I create a new function which encloses the idx variable.
However, idx is only a reference, so at the end of the loop, all rows have the same function and all alert the same value.
One way I solve this at the moment is by doing this:
function GetRowClickFunction(idx){
return function() { alert(idx); }
}
and in the calling code I call
row.click(GetRowClickFunction(idx));
This works, but is somewhat ugly. I wonder if there is a better way to just copy the current value of idx inside the loop?
While the problem itself is not jQuery specific (it's related to JavaScript closures/scope), I use jQuery and hence a jQuery-only solution is okay if it works.
You could put the function in your loop:
for (var idx in data) {
(function(idx) {
var row = $("<tr></tr>");
row.click(function() { alert(idx); });
table.append(row);
})(idx);
}
Now, the real advice I'd like to give you is to stop doing your event binding like that and to start using the jQuery "live" or "delegate" APIs. That way you can set up a single handler for all the rows in the table. Give each row the "idx" value as an "id" or a "class" element or something so that you can pull it out in the handler. (Or I guess you could stash it in the "data" expando.)
Check out jquery's data() method:
http://api.jquery.com/jQuery.data/
Description: Store arbitrary data associated with the specified element.