Using object wrappers to extend the JavaScripts DOM? - javascript

I'm trying to add simple functions to the JavaScript DOM, e.g. an addClass function, I implemented this first with the following code:
Element.prototype.addClass = function(className) {
this.className += ' ' + className;
};
However after much reading (http://perfectionkills.com/whats-wrong-with-extending-the-dom/ was good) it seems this is a terrible way to extend the DOM for a number of reasons.
The above article states:
One of the most common alternatives to this whole mess of DOM
extension is object wrappers
Which is fine, apparently the general consensus is to use Object wrappers if you want to extend the DOM. The problem is I can't find any good examples anywhere on how you actually use object wrappers to extend the DOM ...
Can anybody give me an example of how to do so? Maybe using the above code?

Object wrappers are more expensive than extensions because you need to create a new object, but they are safer.
A simple implementation that wraps only a single element could look like this:
(function() {
window.wrap = function(el) {
return new Wrapper(el);
};
function Wrapper(el) {
this.element = el;
}
Wrapper.prototype.addClass = function(cls) {
if (this.element)
this.element.className += " " + cls;
}
Wrapper.prototype.swap = function(el) {
this.element = el;
}
})();
Then you could make a new wrapper, and to be more efficient, you could reuse it with various elements.
var wrp = wrap(document.body);
wrp.addClass("foo");
wrp.swap(document.body.firstElementChild);
wrp.addClass("bar");
Another feature you could implement would be to add return this; to all the wrapper methods. That way you could chain your function calls if you like.
var wrp = wrap(document.body);
wrp.addClass("foo")
.swap(document.body.firstElementChild)
.addClass("bar");
You could also implement your wrapper to hold multiple elements at numeric indices like an Array, or better, simply hold an Array of elements.

I think that jQuery is a big example of object wrapper. Mainly you just use it like $(domElement) to get some additional functionality.
You can do sth like:
var wrapper = function(el){
return {
go: function(){
console.log('go with', el);
}
}
};
wrapper(someEl).go();

I think that to extend the native behavior in javascript is not good.
And I find another post in the same website you post extending-built-in-native-objects-evil-or-not
So I'll say that I don't like to extend the stuff javascript provide us.

Related

Using jQuery functions in/as higher order functions

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.

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.

javascript JQuery similar selector

Hello guys I want to have something like JQuery selectors. What I mean is this:
JQuery has a selector something like this:
$("#exampleID").JQexampleMethod();
I want to have something similar for my class. for example
selector("#exampleID").MYexampleMethod();
but I can't find out how to do this. I know how to create class and define them. But can't achieve my needs. So far what I have done is this:
var class = new TestClass();
class.selector("#exampleID").MyExampleMethod();
jQuery uses the Sizzle library to evaluate the selectors.
It's not that easy to replicate jQuery because it contains of all kinds of fixes for cross-browser issues and even a whole selector library.
That said, if you disregard those issues, a simple prototype can be as follows:
var selector = function(selector) {
var res = document.querySelectorAll(selector); // fetch elements
res.myExampleMethod = function() { // add a method
return res.length;
};
return res;
};
You could use it as follows:
selector("p").myExampleMethod(); // e.g. 12

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