With jQuery.each(), I can iterate through members of the array:
// This is NOT what I am looking for.
$('.example a').each(function() {
// do a lot of things here
$(this).css('color', 'red');
});
However, I need to apply a function to the jQuery array itself, and not to its members. So I wrote a little plugin to do this:
$.fn.all = function( callback ) { return callback.call( this ); }
$('.example a').all(function() {
// do a lot of things here
$(this).css('color', 'red');
});
Please notice that in the function above, this will be set to the collection of the elements - which is what I require.
Now I'm sure that jQuery should have an elegant way to do this, but I haven't found anything in the documentation, or by Googling.
Is it possible, and if it is, how do I achieve this without using custom plugins?
UPDATE: I can not do $('.example a').css('color', 'red'); directly. I have a dozens of calculations in the function, so I have to use something similar to the plugin I wrote.
I am asking for a callback. The correct answer must provide a callback similar to the custom function.
You don't need a plugin, you can use call directly:
(function() {
// do a lot of things here
this.css('color', 'red');
}).call($('.example a'));
Or consider passing the array-like object as an argument
(function(arrayLike) {
// do a lot of things here
arrayLike.css('color', 'red');
})($('.example a'));
Or if you prefer a jQuery way
$.each([$('.example a')], function(){
// do a lot of things here
this.css('color', 'red');
});
Shouldn't
$('.example a').css('color', 'red');
be enough?
That would be the straight-forward (jQuery-esque) way to do what you want:
var $collection = $('.example a');
$collection.each(function() {
// do a lot of things here
$collection.css('color', 'red');
});
//And another reason I dislike jQuery's implementation of each().
//but most of all, wrong argument-order and the abuse of this to pass the current value/node
Here my prefered implementation. Mostly like Array.prototype.forEach(), and the nice parts of $.each().
$.fn.forEach = function(fn, scope){
if(scope != null) fn = fn.bind(scope);
for(var i = 0, len = this.length; i < len; ++i)
if(false === fn(this[i], i, this)) break;
return this;
}
and your code would be like:
$('.example a').forEach(function(node, index, $collection){
//and don't use `$(this)`, since `this` most of the time references `window`
//unless you've bound the function to a scope, or passed one to forEach
var $node = $(node);
$collection.css('color', 'red');
});
this
$('.example a')
returns collection of matched elements. And this
$('.example a').css('color', 'red');
sets color red to all elements in the collection.
That's how jQuery works.
I think that is not possible getting all the calculations at the same time, you should use threads for that, or a webWorker. You can get more info about this in mdn.
You can cache selector, utilize $.queue()
var elems = $(".example a");
$.queue(elems, "q", function() {
$(this).css("color", "red");
console.log(this)
});
$.dequeue(elems, "q")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<div class="example">
a
b
c
</div>
Related
My jQuery code is:
$('#edit').click(function(){
var data = $("#list :input").serialize();
$.post($("#list").attr('action'), data, function(json)
{
currentRow = json.rowArr[0];
$("#id").val(currentRow.id);
$("#id_disp").val(currentRow.id);
$("#shop").val(currentRow.shop);
$("#category").val(currentRow.category);
$("#item").val(currentRow.item);
$("#qnty").val(currentRow.qnty);
$("#unit").val(currentRow.unit);
$.each($("#price_based_on").children('option'), function(index, val) {
if(this.value.toUpperCase()==currentRow.price_based_on.toUpperCase())
{
console.log("Match");
this.prop("selected", true);
}
});
$("#mrp").val(currentRow.mrp);
$("#sellers_price").val(currentRow.sellers_price);
$("#last_updated_on").val(currentRow.last_updated_on);
},"json");
});
Among this, the only thing of interest are the lines:
$.each($("#price_based_on").children('option'), function(index, val) {
if(this.value.toUpperCase()==currentRow.price_based_on.toUpperCase())
{
console.log("Match");
this.prop("selected", true);
}
});
On using the statement this.prop("selected", true); I get the error:
Uncaught TypeError: this.prop is not a function
Why does this happen when .prop() is clearly a function that exists? How do I fix it?
$.each is used to iterate over an object or array. If you want to iterate over the nodes in a jQuery object, use .each like this:
$("#price_based_on").children('option').each(function() {
... code here
});
Inside the call back, this refers to the native DOM element (which doesn't have a prop method, so you probably want to do something like this to get a reference to the jQuery object that holds the DOM node:
$("#price_based_on").children('option').each(function() {
var $this = $(this);
$this.prop('selected',true)
});
this is not a jquery object, you need to add jquery selector $() around to make it jquery object, so change it to $(this).
this is not a jQuery object. Use $(this)
$(this).prop("selected", true);
I have a function that I want to remove jQuery from:
var hunt = new RegExp(action.search);
$('a').filter(function() {
return hunt.test(this.href);
}).click(doStuff);
I am trying to rewrite this function using vanilla javascript. this is what I have so far but it is not working
var hunt = new RegExp(action.search);
var aSelector = document.getElementsByTagName('a');
var arr = Array.prototype.filter.call(aSelector, function(el){
return hunt.test(el.href)
});
for(var i=0; i<arr.length; i++){
var elem = arr[i].getAttribute('href');
elem.onclick = function(){
doStuff();
};
}
I am guessing that the jQuery selector behavior is slightly different than document.getElementsByTagName but I really have no idea what is going on. Any help would be greatly appreciated. Thanks
better use querySelectorAll() instead of getElementsByTagName(), besides that, I think the code is self-explaining. Ask if something is unclear
//a very basic utility to fetch Nodes by css-selectors and return them as an Array
//- shorthand for document.querySelectorAll()
//- document.querySelectorAll() returns a NodeList wich lacks the Array-methods
//- second argument to provide a Node as the context of the query
function $$(selector, context){
return Array.from((typeof context === "object" && context || document).querySelectorAll(selector));
}
//now we can use the regular Array-methods
$$('a').filter(function(node){
return hunt.test(node.href);
}).forEach(function(node){
node.addEventListener("click", doStuff);
})
or this way:
$$('a').forEach(function(node){
if(hunt.test(node.href)){
node.addEventListener("click", doStuff);
}
});
I create a simple jQuery extension(it's my first).
(function($){
var MyClass = function(opt){
//..
};
//one of the methods of my extension
$.fn.myExtension = function(opt){
this._ext = new MyClass(opt);
return this;
};
$.fn.myExtensionOtherMethod = function(){
if(this._ext){
//do something ..
}
return this;
};
}(jQuery));
//using ..
$(document).ready(function(){
$('#selector').myExtension({
//options ..
});
$('#selector').myExtensionOtherMethod();
});
when i invoke method $('#selector').myExtensionOtherMethod();, this does not contains this._ext variable. I know that this is other scope, but i know that there is some way to access that variable in both methods.how can i do it?
This isn't really a scope issue. This is because the jQuery prototype $.fn gives you a jquery object as this. Even though you are selecting the same element each time its a new jQuery object set as the context so that property is gone. You can put that property on the DOM element and achieve the outcome you want.
(function($) {
var MyClass = function(opt) {};
//one of the methods of my extension
$.fn.myExtension = function(opt) {
this[0]._ext = new MyClass(opt);
return this;
};
$.fn.myExtensionOtherMethod = function() {
if (this[0]._ext) {
//do something ..
}
return this;
};
}(jQuery));
//using ..
$(document).ready(function() {
$('#selector').myExtension({
//options ..
});
$('#selector').myExtensionOtherMethod();
});
This is just a quick example above. If your selector finds more than one element you should loop though them. But I only grabbed the first index since you were selecting by ID.
Fiddle: https://jsfiddle.net/AtheistP3ace/gd1ehk0d/
As mentioned above by #charlietfl, I agree with that comment. Happy to explain why what you did didn't work but there may be better ways to achieve what you are looking for.
Here is my code:
var b = $(slipStream.conf.mainVis).find('p#prev');
b.click(function() {
slipStream.slideLeft();
return false;
});
b = $(slipStream.conf.mainVis).find('p#next');
b.click(function() {
slipStream.slideRight();
return false;
});
b = $(slipStream.conf.controls).find('li img');
console.log(b);
for (var l in b) {
l.click(function() {
var visIndex = l.index();
console.log(visIndex);
});
};
The first two bindings go through, no problem. But I can't loop through a collection and bind something to each member? (the console is telling me that "l.click is not a function.") Is this a limitation of jQuery or is my code off? This seems like it would be the way to do it, though...
When you enumerate over a jQuery object, the values being enumerated are actual DOM nodes and not jQuery wrappers. Therefore, they don't have a click method but you can wrap them again to get all the usual functionality.
Of course this is not necessary because you can simply attach a wrapper directly from your initial jQuery instance:
$(slipStream.conf.controls).find('li img').click(function() {
var visIndex = $(this).index();
console.log(visIndex);
});
This is the classic "loop variables don't work properly in callbacks" bug.
Your variable l no longer has the originally supplied value by the time the callback is invoked - it has whatever final value was assigned in the last pass through the loop.
[FWIW, l isn't actually a jQuery object, so you have to wrap it - $(l) to use it with jQuery]
The usual fix to the loop bug is to create an additional closure that returns a function bound to the current value:
for (var l in b) { // NB: don't use `for ... in ...` on array-like objects!
var make_cb = function(n) {
return function() {
var visIndex = $(n).index();
console.log(visIndex);
}
}
$(l).click(make_cb(l));
};
Fortunately, you don't need a loop at all - you can have jQuery automatically add the callback to every element by itself:
b = $(slipStream.conf.controls).find('li img');
b.click(function() {
var visIndex = $(this).index();
console.log(visIndex);
});
Could it be that the problem is the forloop. .click is part of the jQuery, so you must be sure that it's called on element that is wrapper with jQuery.
$.each(b, function (index, element) {
$(element).click(function() {
});
};
With each() you can iterate through a set of jQuery objects:
$(slipStream.conf.controls).find('li img').each(function(){
$(this).click(function() {
var visIndex = $(this).index();
console.log(visIndex);
});
});
$(this) will match the currently indexed object from the collection.
I think I have mistaken some fundamentals here, because I think this should work. I am trying to to through the child p and div elements of the matched set, and remove those which fail to meet the required wordcount from the matched set.
I have tested the wordCount plugin, and the if statement it is being used it, and all seems to be working fine, but my element is not being removed from the matched set.
(function($){
$.fn.extend({
textBlocks: function(count){
var JQ_Object = $(this);
if(!count) count = 100;
return this.each(function(){
$(this).find("p, div").each(function(){
if($(this).wordCount()<count){
var x = $(this);
JQ_Object.not(x);
};
});
return JQ_Object;
});
}
});
})(jQuery);
Here is the wordCount plugin, just in case you wondered:
(function($){
$.fn.extend({
wordCount: function(){
return $(this).html().split(" ").length;
}
});
})(jQuery);
I made a few changes... see fiddle for working example and code for comments.
http://jsfiddle.net/8PXpt/
(function ($){
$.fn.extend({
wordCount: function (){
//Don't need $(this), this already refers to the jQuery object
//Always trim .html() and .text() when using .split()
//May want to use .text() instead of .html() - I leave that to you
return $.trim(this.html()).split(' ').length;
}
});
})(jQuery);
(function ($){
$.fn.extend({
textBlocks: function (count){
var collection = this;
//Check that a number was passed
//"50" would break your extension
if(typeof count !== 'number') {
count = 100;
}
//Removed $('div, p') - this should be part of your call
//See ready function below
this.each(function (){
if ($(this).wordCount() < count){
//This could be double assignment
//but better safe than sorry
collection = collection.not(this);
};
});
//Return what is left (what passed)
return collection ;
}
});
})(jQuery);
$(function() {
//Here is where you define your selector... in your case 'div, p'
$('div, p').textBlocks(2);
});
Have you tried $(this).remove() rather than JQ_Object.not(x);
I think .not() removes them from the selection rather than from the HTML... unless that's what you're trying to do
You're creating a new JQ_Object in the internal each, so I'm not sure if it would modify the original JQ_Object. I'm not 100% on that though. Try JQ_Object.not(this).
This assumes, however, that .each is synchronous, which I'd hope it isn't. If that's the case, you'd need to make use of jQuery's while function.
This should give you the desired result, but I'd be wary each being asynchronous.
return $(this).find("p, div").each(function(){
if($(this).wordCount()<count){
JQ_Object.not(this);
};
});
EDIT:
I'm not to sure about the above code. What I'd do is use a callback. This assumes a callback is passed in to your plugin.
$(this).find("p, div").each(function(){
if($(this).wordCount()<count){
JQ_Object.not(this);
};
}).when(function () {
callback(JQ_Object);
});