Using jQuery functions in/as higher order functions - javascript

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.

Related

Write a function that can itself take an argument, then return a value - but also later be extended with new methods (similar to jQuery)

I am trying to figure out how jQuery is both a function which accepts an argument and returns a value (selector -> array of elements), and also an object that can be extended with new methods:
$.fn.myMethod = function() {//do stuff}
As a company we are moving away from jQuery, because vanilla JS is so useable. However we have some jQuery plugins I wrote that we would like to keep using. I am re-writing them, but would like to keep the syntax for using them similar to how it was in JQ. I don't want to extend the Element object (element.myPlugin()) for obvious reasons, so I was thinking I would create my own wrapper like JQ. Ideally I could define the base/default function that returns an array of DOM elements like so:
const __ = function(element) {
if (typeof element === 'string') {
return document.querySelectorAll(element)
}
return [element]
}
But then later, this function could be extended with new methods:
__.myNewPlugin = function(text) {
this.forEach(el => el.innerText = text)
}
Where this is the array of DOM elements returned by the base/default function, so that later the rest of my team could use the new method like so:
__(document.querySelector('.thing')).myNewPlugin('Hi SO')
-or-
__('.thing').myNewPlugin('Hi SO')
If for some reason you think this is a bad idea, I'm happy to hear your reasoning, but please also post an example of how this is achieved. It's obviously possible (because JQ does it), so even if I decide not to go this route, I'd still like to learn how something like this could be implemented.

Finding all objects with a jquery plugin applied

Is there a way to get a list of all objects using a specified plugin? I know i can add a class to each element when it's applied but i was wondering if there was an existing way...
thanks,
If you want to do this without using classes, you might want to sniff the plugin calls, like this:
var elemsCalled = []; // this will contain all elements upon which the plugin has been called
var orig = $.fn.somePlugin;
$.fn.somePlugin = function() {
elementsCalled.push(this);
return orig.apply(this, Array.prototype.slice.call(arguments)); // for chaining, as Alnitak noted
}
Now, whenever you call $.somePlugin, the element you call it on would be added to elemsCalled.

Using jQuery, is is possible to watch a DOM element and report each time something is done to it?

I'm working on a site containing a confusing chain of asynchronous effects. Often, effects do/undo each other several times before stopping. I am having a very hard time following the spaghetti code.
Is it possible to set a callback to run any time a DOM element is manipulated so I can follow the chain of effects the code is applying to it?
Edit: I am currently adding dozens of console.logs everywhere and wanted a cleaner approach.
One approach is hooking into the core jQuery functions and log the information you want. Here's some very rough code on a few functions that i've hijacked i.e hide and animate.
var log_for_functions = 'hide animate'.split(' ');
$.each( log_for_functions , function(){
var function_name = this;
var original_function = $.fn[ original_function_name ]; // get a reference to old function name
$.fn[ function_name ] = function () {
var r = original_function.apply(this, arguments);
console.log( function_name + ' called on element ', this );
return r;
}
});
Now if you hide an element using jquery $('#test').hide() then you will get the following output.
hide called on element [context: #test, selector: "#test"]
You can change this to fit your needs and even hook into other functions.
Not without setInterval.
You could just have the code that applies an effect also note what changed.
You could do this with data attributes.
<div data-state="some state" ...
console.log(targetElement.getAttribute("data-state");
targetElement.setAttribute("data-state","changes");
MakeChanges(targetElement);
The following article introduces two techniques that might work in your case, Mutation Observers and Keyframes:
http://www.backalleycoder.com/2012/08/06/css-selector-listeners/
For each technique the article points to the related library.

Create my own classList object when the browser does not implement it itself

I have JavaScript code that adds and removes classes using the classList object.
The code is up and working correctly as expected until I received complaints about it because it does not work in Opera and in IE8.
I don't want to use any frameworks or code libraries. I'd like to reduce to a max the code I have to change in my program so I wanted to make my own classList object and add it to the node object. I already have this code:
if (typeof(document.createElement('input').classList) == 'undefined') {
HTMLElement.prototype.classList = new function () {
this.add = function (addClass) {
this.className += addClass;
}
}
}
which, obviously, does not work.
The problem is: I can't access the HTMLElement.className of this object like this. How can I accomplish that? How can I make this object to work similar to or exactly like the original?
Mozilla has listed a classList shim here:
http://developer.mozilla.org/en/DOM/element.classList
There's quite a bit going on in the code, but looks to be what you are after.
Instead of extending the prototype you can just create a helper function that returns the values you need:
classList = function ( elem ) {
return elem.className.replace(/\s+/g,' ').split(' ');
};
Then instead of calling elem.classList you'd just call classList(elem), meaning it doesn't work exactly like the original but it returns the same data.

passing javascript object to function in a jquery way?

Yeah so I've been messing around with javascript a while but only recently got into stuff like object orientation, prototyping and using objects for all functions and vars.
But, many frameworks like jQuery or extJS have something I have yet to grasp, you can define an object by using a built in function for searching the dom, example:
var a = $('#mydiv');
and then you can do a.click(function);
So my question is, how can I create a "framework" of sorts where I can write code in that style, like example:
var mydiv = document.querySelector('mydiv');
mydiv.neph(args,args);
So I have my object defined, in this case it's a dom element or whatever, now I pass it to my function "neph" with arguments, I want to create code that allows me to do this. But since mydiv does not have any function, it only has in this case the dom element right so mydiv.neph does not exist, but in jquery you can define whatever var and .click or .mouseover or whatever does exists within the object as functions? Confusion ensues! :D
Ok sorry if this is a retarded question btw =P
jQuery and other libraries define a function called $ that takes several optional parameters. The object returned by calling $ is not a DOM element, but a jQuery object wrapping a DOM element up with a set of convenient functions.
You can do something similar yourself:
<html>
<body>
<input id="derp" type="text"/>
<script type="text/javascript">
function $(id)
{
return new myLibrary(id);
};
function myLibrary(id)
{
this.el = document.getElementById(id);
};
myLibrary.prototype.help = function()
{
alert(this.el.id);
return this;
};
// Woah! My own fake jquery!
$("derp").help();
</script>
</body>
</html>
jQuery is far more sophisticated, of course. For example, it will use apply and call to set this correctly in calls like jQuery.each.
You need to create a Prototype in javascript. This is what allows you to add a function to an object that's already defined (i.e. the .click() function that you gave as an example).
You can have a look at the jQuery code, it's open source. It's not the simplest code, but you can still see how it works and how they do it.
Mike's comment is a good answer: Look at jquery or Ext-Core's sources.
Maybe what you're missing is that, in jquery, for instance $() returns a jquery object, which wraps the plain vanilla DOM node, providing extended functionality.
In jQuery, $ is just an alias to the jQuery object. So when you call $('#mydiv'); you're really calling a function like jQuery('#mydiv'); So part of what makes jQuery so cool is that every the $() function returns this, which means when you call the $() you're getting a handle to the jQuery object and all of the methods it has on it. That is what allows you to do something like this:
var a = $('#mydiv');
a.click(function() { // etc etc etc });
So to pull off your example:
var mydiv = document.querySelector('mydiv');
mydiv.neph(args,args);
You'd have to create an object that has a function called neph on it and return that object in the context of mydiv when you call querySelector.
var myFramework = function() {
this.context = undefined;
this.neph = function(arg, args) {
console.log(arg, args);
}
};
var myCuteFunction = function(element) {
var f = new myFramework();
f.context = element;
return f;
}
// ...
var x = myCuteFunction('#mydiv');
x.neph(arg, args);
Since nobody has really answered about Ext, you can easily extend the element wrapper prototype:
Ext.override(Ext.Element, {
myMethod: function(a, b){
console.log(a, b, this);
}
});
"this" will refer to the Ext.Element object.

Categories