I use the following jQuery structure a lot so I want to write it shorter (EDIT: the addClass and removeClass are EXAMPLE functions, I want to know a general ternary way to apply different functions to an object, so do not tell me about toggleClass which doesn't even work like below code):
if(something){
obj.addClass('class');
}else{
obj.removeClass('class');
}
I want to write it in a single line, so I would like to know how to apply jQuery functions conditionally. Something like the following using a ternary operator, only this doesn't work yet:
$[something?'addClass':'removeClass'].apply(obj,'class');
This is easily done in regular javascript but how do I structure the above correctly to work in jQuery?
EDIT: I am not searching for two separate calls such as:
something ? obj.addClass('class') : obj.removeClass('class');
Just put it to equal the something, if it is true it will triger otherwise wont triger.
$( this ).toggleClass( "class" ); = something;
What about writing a helper function?
function ternary(object, condition, a, b) {
var method = condition ? $.fn[a] : $.fn[b];
var args = arguments.slice(4);
return method.apply(object, parameters, args);
}
Then you could do
ternary(obj, something, 'addClass', 'removeClass', 'my-class-name');
ternary(obj2, somethingElse, 'slideUp', 'slideDown');
Related
I am adding / removing a class from an element’s classList based on a variable’s truthiness. However, I’m doing it in what appears to be an obtuse way:
if (myConditionIsMet) {
myEl.classList.add("myClass");
} else {
myEl.classList.remove("myClass");
}
Is there a way in which I could make this more sexy and dynamically call the add / remove chained function for example with a conditional operator such as:
myEl.classList.{myConditionIsMet ? add('myClass') : remove('myClass')};
The above is pseudocode, of course, and I would like as plain JS as possible.
There’s a toggle method on .classList which takes a second argument (force).
This boolean argument essentially takes a condition that adds the class if true, and removes the class if false.
myEl.classList.toggle("myClass", myConditionIsMet);
Your pseudocode js
myEl.classList.{myConditionIsMet ? add('myClass') : remove('myClass')};
can be translated to actual js
myEl.classList[myConditionIsMet ? 'add' : 'remove']('myClass');
which is not particularly readable, but does exactly what you described.
For readability, I would look at the toggle method.
I couldn't find the syntax something like this anywhere:
var mz = jQuery.noConflict();
mz('#zoom01, .cloud-zoom-gallery').CloudZoom();
This means: jQuery.noConflict()('#zoom01, .cloud-zoom-gallery').CloudZoom();
And something like this:
$(window)[this.off?'off':'on']("scroll", fixDiv );
So, I'm wondering about the syntax something like of these:
jQuery.noConflict()(syntax) and $(window)[syntax](syntax) and I also think there might be something like this $(selector){syntax}
Can anyone elaborate of those syntax?
The best place to start is the documentation
$.noConflict()
Many JavaScript libraries use $ as a function or variable name, just
as jQuery does. In jQuery's case, $ is just an alias for jQuery, so
all functionality is available without using $. If you need to use
another JavaScript library alongside jQuery, return control of $ back
to the other library with a call to $.noConflict(). Old references of
$ are saved during jQuery initialization; noConflict() simply restores
them.
In other words, noConflict() sets a variable to equal jQuery, so this
var mz = jQuery.noConflict();
mz('#zoom01, .cloud-zoom-gallery').CloudZoom();
is the same as
$('#zoom01, .cloud-zoom-gallery').CloudZoom();
or
jQuery('#zoom01, .cloud-zoom-gallery').CloudZoom();
noConflict() does not directly take selectors, it's just a function that sets jQuery in a certain scope to a variable so you can have multiple versions of jQuery (which you shouldn't) or use other libraries that also uses $ for something, it does not mirror the selector engine or anything else, even if it might seem so at first glance, it simply returns an instance of jQuery
In javascript there is dot notation and bracket notation, so an object can be accessed as
object.propertyName
or
object['propertyName']
as everything in javascript is an object, even jQuery methods, they can be accessed as
$('#element').fadeIn(200);
or
$('#element')['fadeIn'](200);
it's the same thing, so doing
$(window)['on']("scroll", fixDiv );
is the same as
$(window).on("scroll", fixDiv );
the advantage of using brackets is that they can contain any string, even variables, or in this case ternary statements, or the returned result of a function
var event = 'on';
$(window)[event]("scroll", fixDiv );
or
var event = this.off ? 'off' : 'on';
$(window)[event]("scroll", fixDiv );
that one also uses this, which in the global scope would be window, and it's the same as
$(window)[this.off ? 'off' : 'on']("scroll", fixDiv );
The ternary statement itself is just a fancy condition, and this
var event;
if (this.off) {
event = 'off';
} else {
event = 'on';
}
is exactly the same as
var event = this.off ? 'off' : 'on';
Added for the edited question :
jQuery() or $() is a function, something we can tell from the parenthesis, so it's something like
function jQuery(arguments) {
// do something
}
which can be called as
jQuery(some_arguments);
and as var $ = jQuery one can also do $();
Now that we know it's a function, it makes sense that we can do
$('#element_id')
and internally jQuery checks what kind of argument we passed, it sees that is's a string, and it's starting with #, so it's an ID, and then jQuery can do document.getElementById() and get that DOM element, and at the same it wraps that element in a new array-like object, usually referred to as a jQuery object.
We can also pass in a DOM node, array, object or anything else, and jQuery tries to figure out what it is, and wrap in that jQuery object for us to use with other jQuery methods, so this :
$({x:10, y:20})
is the same as
var obj = {x:10, y:20};
$(obj)
and its turned into one of those jQuery objects with the properties x and y. Passing in an object like this means we can chain on methods, and those properties are available in the methods.
$({x:10, y:20}).animate({x:50}, 1000);
And that's basically how it works, simplified a lot.
As for passing objects to methods, that's a very common way to pass arguments.
To see how it works, it's easiest to create a method:
$.fn.doStuff = function(argument) {
this.css(argument);
}
inside a jQuery plugin, this is the jQuery object, and we can now use the mothod above that does nothing more than pass the arguments to jQuery's css().
We know we can pass an object to css() like this :
$('#element').css({left: '10px', top: '20px'});
so using our plugin we can do the same
var obj = {left: '10px', top: '20px'};
$('#element').doStuff(obj);
and it ends up doing exactly the same thing. Of course, we could do anything with the object :
$.fn.doStuff = function(args) {
if ( typeof args == 'string' ) {
alert(args); // if it's a string, just alert it
} else if ( typeof args == 'object' ) {
for ( var key in args ) { // if it's an object, iterate
this[0].style[key] = args[key]; // and do something
}
}
}
foo['bar'] syntax is to get the property bar from object foo.
foo() is to execute the function foo.
And you can combine these as you wish.
jQuery.noConflict() returns a function so you could execute the result by jQuery.noConflict()(syntax).
$(window) returns an object so you could get a property from it by $(window)[syntax], and if the property is a function, then you could execute it by $(window)[syntax](syntax).
This is just javascript syntax.
person.name is exactly the same as person["name"]
The same happens with methods
$(window).on(...) is exactly the same as $(window)["on"](...)
One cool thing about the second way is that you can make the member name variable, for example:
So doing:
var windowMethod = "on";
$(window)[windowMethod](...)
is the same as
$(window)["on"](...)
And you can have an expression inside the brackets, so this:
$(window)[this.off ? 'off' : 'on']("scroll", fixDiv );
would be exactly the same as doing this:
if(this.off)
$(window).off("scroll", fixDiv);
else
$(window).on("scroll", fixDiv);
But the former is shorter.
Hope this helps. Cheers
PS: The jQuery.noConflict()(syntax) is straightforward, .noConflict() just returns a function and then we append some other parens to call it just as any other function.
I was trying to use $.fn.show (and other jQuery functions) within higher-order functions.
What I originally wanted to have was a function that applies a given function to all elements returned by a collection of other functions applied to a given element. Something that would look like this:
function mapOn( func, genratingFunc, element ){
$(generatingFuncs).each(function(){
var buf = $(element);
while(buf.length){ // run as long as elements are returned
func(buf);
buf = this(buf);
}
});
}
I needed such a function to apply some functions to a couple of DOM nodes and their parents and/or children in a handy, expressive way. Let's say we want to hide the node with the ID hideMyFamily and its children. I don't know any handy way to do this with jQuery so I'd run hide() on $("#hideMyFamily").children() and on $("#hideMyFamily").children().children() and so on until the length of the collection was 0 (and on $("#hideMyFamily") itself of course).
Thing is, running mapOn( $.fn.show, [$.fn.children], $("#hideMyFamily") ) won't do the job since you apparently cannot just apply $.fn.show to an element/collection.
So what I came up with is this:
For each of the jQuery's functions that I need to specify another function (within global scope) that looks like this:
function _show(e){ $(e).show(); }
For each of the jQuery's "generating" functions I specify another "work-around function":
function _id(e){ return $(e); }
function _children(e){ return $(e).children(); }
And then I can specify my "multiMap" function which looks like this:
function multiMap(func, generators, elem){
$(generators).each(function(){
var buf = $(elem);
var buf2 = [];
while (buf.length && buf[0] !== buf2[0]) {
func(buf);
buf2 = buf;
buf = this(buf);
}
});
}
Now I can run my handy function multiMap(_show, [_id, _children], "hideMyFamily") to hide the element itself and all of its children.
Now, to get to the point, my question is: Is there any more elegant way to achieve the desired behaviour? Is there any jQuery magic I didn't take into account?
tl;dr Is there a handy way to use jQuery's functions like show() and hide() on nodes/collections in a way like $.fn.show( $("someElements") )?
Yes, it is possible.
You can do $.fn.show.call($("some-elements")).
I haven't fully gone through your more elaborate example, but that seems to be what you're looking for. And for your final example, you could write things as:
multiMap($.fn.show, [_id, _children], "hideMyFamily")
and then in mutliMap do f.call or something like that and I believe that would work.
I am using the following function closure in a jqgrid (a jquery grid) to retain changes in edits when paging in a variable called 'retainedChanges'- does this look ok; Im i breaking any good practices in javascript;
the code works alright just want to make sure I dont introduce features that can break in the future
(function($){
var retainedChanges;
retainedChanges = new Array();
$.retainChangesOnPaging = function(){
var changedCells = $('#grid').jqGrid('getChangedCells');
// loop over changedCells array, removing duplicates if you want to...
return retainedChanges.push(/* this is inside the loop; push current value to array*/);
....
}
$.getRetainedChanges = function(){
return retainedChanges;
}
})(jQuery);
This works fine, although you should probably accept jQuery as an argument:
(function($){
This way, even if the $ symbol is being used for something else outside of your closure, it won't effect your code inside the closure.
2 more things:
1) You should declare and assign you variable together, and use [] instead of new Array().
2) You're missing a $ symbol here: ('#grid').
For a full rundown, look at this:
(function($){
var retainedChanges = [];
$.retainChangesOnPaging = function(){
var changedCells = $('#grid').jqGrid('getChangedCells');
// loop over changedCells array, removing duplicates if you want to...
return retainedChanges.push(/* this is inside the loop; push current value to array*/);
....
}
$.getRetainedChanges = function(){
return retainedChanges;
}
})(jQuery);
You are passing jQuery into a function that has no arguments and never uses the jQuery object passed in. You may have meant:
(function($){
Other than that it looks fine.
There are several things you could improve:
1) You pass jQuery to the function, but do not use it (you use global object $, if it is defined). Modify your code to accept one parameter, named $:
(function($){
2) You can shorten retainedChanges declaration:
var retainedChanges = new Array();
3) If you are trying to write jQuery plugin, then follow the following tutorial: jQuery: Plugins/Authoring
If not, then maybe use different global object than jQuery?
function divlightbox(val)
{
if(val)
{
val=val.replace( /^\s+/g, "" );
var count_js=0;
var big_string='';
document.getElementById("video_lightbox").innerHTML="";
document.getElementById("divlightbox").style.display = "block";
$("#video_lightbox").css({"height":"430px","top":"10%","width":"480px"});
I found out that the error is in the above. My question is can't I use jQuery and traditional JavaScript at same time? I have done coding like this numerous times and never ran into a problem like this. I used to use jQuery methods like .hide() and .css() inside JavaScript functions but this time it doesn't work.
Thanks in advance.
While the other answers fix the specific problems, I don't think the OP's question (in bold) is really answered here, as depending on the specific context, $ may possibly not be defined as a jQuery object yet (having had this problem myself a few times now.)
In which case you would need to do something like:
function divlightbox(val) {
// ...
// just use jQuery instead of $ one time
jQuery("#video_lightbox").css({"height":"430px","top":"10%","width":"480px"});
}
OR
function divlightbox(val) {
// define the $ as jQuery for multiple uses
jQuery(function($) {
// ...
$("#video_lightbox").css("height":"430px");
$("#video_lightbox").css("top":"10%");
$("#video_lightbox").css("width":"480px");
});
}
jQuery is JavaScript so YES. Instead .innerHTML="" just use .empty(). Instead .getElementById() use $('#..') and so on.
to do things like hide(); and css() you need jquery objects. you can't do them to dom elements.
so you could do $('#video_lightbox').html("");
or
$('#video_lightbox').empty();
You must provide error in javascript console.
1) Do you pass a val argument to divlightbox function()? When do you call it?
2) why do you use the same identifier divlightbox both for a function and for a div id? Change name to the function please, maybe the problem could be here.
3) Always check if video_lightbox and divlightbox exist before accessing them.