I need to know how to make something like a jQuery function, not with the CSS selectors, just like this:
function getId( item ) {
return document.getElementById( item );
}
getId('elementID').firstFunction('property').secondFunction('property');
but I don't know how exactly do I send getId result to firstFunction to make something to the getId() element and then make the secondFunction work, I've been trying with prototype but I can't make it work.
You just need to ensure that all of your methods that you want to be able to chain accept the same context as they output, in your case a DOM element.
So, firstFunction and secondFunction should both expect this to be a DOM element and should return the DOM element as well. This means, however, they you will need to modify the built in DOM element object and add your own methods, which is generally a bad idea. This is why jQuery wraps everything in a wrapper object that contains the methods available as an API. Those methods all act on this (which must be of the same special object type) and return a (usually modified) object of the same type.
Check out a lighter-weight example here: http://buildingwebapps.blogspot.com/2012/01/creating-lightweight-dom-javascript.html and some additional reading here: http://kendsnyder.com/posts/element-wrappers-in-javascript
Related
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.
In the Mootools docs, regarding Element, I cannot catch the semantic area of the document element, for which I cannot find any reference in the Mootools Api. Is it just DOM Api?
For example $ accepts three parameters, the first of them is element. How do I define an element?
Further, the docs offer some advice: document.getElementById(‘foo’), document.id(‘foo’), $(‘foo’). So I understand document is a pretty important part of Mootools, but I do not understand where it overlaps to the classic DOM API, how far it is extended and so on.
So, MooTools has the concept of Types. Types are hashes built on top of either custom objects or by extending prototypes of Natives (Array, Element, String, Function, Number, to name a few) or appending methods to Natives (Date, Object).
The Element Type (http://mootools.net/core/docs/1.5.1/Element/Element) is the abstraction around HTMLElement as well as sub types like HTMLInputElement, all part of the DOM interface.
document itself is inheriting from Element to a degree - prototype methods available on any HTMLElement will be available to call on document as well, though they may not always be applicable. For example, addEvent will work and makes sense but tween or show etc won't.
Let's assume you mean document.id
Extending natives is considered harmful because it can result in unwanted side effects. Additionally, not every browser exposes the HTMLElement prototype for modification equally. Whereas in evergreen browsers (read, not IE 8 and lower), HTMLElement is OK to change, in IE6-7 it was not (read-only) and in IE8, it only extends some types of elements whereas others have no link to the augmented prototype chain.
So, imagine you have this:
<div id="foo">foo</div>
and a corresponding object:
var foo = document.getElementById('foo');
Since the constructor of foo is Element and the prototype of foo is Element.prototype, if you called foo.addEvent it will look up the chain, reach the Element.prototype.addEvent method and call it.
But because of IE6,7,8, the above may not work well or at all - MooTools devs chose a radical way of beating this issue by doing something simple: shortening the scope of the property lookup chain in these browsers.
This is done by actually setting a reference on the foo object itself pointing to all the methods and properties on the Element.prototype object.
you can think of doing it this way:
foo.addEvent = Element.prototype.addEvent.bind(foo);
so even if foo does not have access to the proto chain, it will still be able to call the method.
This so called 'extending' takes place the first time MooTools passes an element object.
So, in our case if in IE, you'd do:
foo = $(foo); // or document.id(foo);
Upon passing the element, it gets decorated with the references of the methods that you can now call
here's a better example:
var foo = document.getElementById('foo'), bar;
console.log(foo.hasOwnProperty('addEvent')); // false in all browsers
try {
foo.addEvent('click', bar = function(){});
}
catch(e){
console.log(e);
foo = $(foo);
foo.addEvent('click', bar = function(){});
console.log(foo.hasOwnProperty('addEvent')); // true in IE6,7,8
}
// by now it's already extended, so we can safely call it.
foo.removeEvent('click', bar);
further more: not only does document.id (which $ will alias to) enable proto methods use, it also sets up a Slick.uuid to recognise the element, which is then used to enable Element Storage via the data API (Element.prototype.store/retrieve/eliminate). This way, the Storage object has a unique key that maps to exactly one element in the DOM so you can stick stuff on there - it's the same implementation as jQuery's .data API.
FINALLY, document.getElementById is just JS api that gets you an element object. that's untouched by MooTools.
TL;DR; document.id(mixed) prepares elements for use with MooTools in a cross browser way and sets up storage.
Passing an element extends and returns the element object. Passing a string finds an element by ID if possible, then extends result and returns object.
You can also use new Element() constructor to create an element on document.createElement - as well as any HTMLElement you may have gotten from any QSA or DOM API.
Say I have a map on an array of elements. The callback function takes the index and the value at that position in the array.
If I wrap the array element that the callback receives in $(), it behaves as I expect. If I use it without wrapping it in $(), it gives an error.
var nonHiddenElements = $( "form :input" ).not(':hidden');
nonHiddenElements.map(function(index, element){
input_id = $(element).attr('id'); // this works
input_id = element.attr('id') ; // this gives an error
})
Can someone explain how this works.
Is this a jQuery quirk, or a JavScript thing?
What type of objects does my nonHiddenElements array contain exactly?
What is element that gets passed to the callback?
And mainly what is the $() doing?
You need to understand how jQuery actually works. I will try to explain it briefly.
$ is nothing but a normal javascript function. jQuery === $, is just a function with a fancy name. This function does a lot of different things, depending on what you pass in it. For example if you pass a string it will be treated as CSS selector and jQuery internals will try to find corresponding DOM elements. Or if you pass a string starting with < and ending with > jQuery will create a new DOM element by provided HTML string.
Now if you pass a DOM element or NodeCollection of DOM elements, it/they will be wrapped into jQuery instances so that they can have a jQuery prototype methods. There are many prototype methods jQuery offers. For example text, css, append, attr - those are all methods of jQuery prototype. They are defined basically like this (simplified):
jQuery.prototype.text = function() { ... }
Normal DOM elements don't have those convenient methods jQuery provides. And inside of methods like map or each if you check this value or element parameter like you do, you will see that they are actually not jQuery instances:
element instanceof jQuery // => false
and of course you can't use instance methods with not an instance.
So in order to use jQuery prototype methods you need have a jQuery instance, which you can obtain if you call jQuery function with DOM element passed in it:
$(element) instanceof jQuery // true
Javascript is a programming language.
jQuery is a JavaScript Library.
With jQuery:
$("some element")
In native JavaScript you would have to do something like this.
getElementById('elementByID')
Explained in detail here: https://developer.mozilla.org/en-US/docs/Web/API/document.getElementById
MDN is a great resource for beginners. https://developer.mozilla.org/en-US/docs/Web/JavaScript
I've been wondering for a little while how jQuery acts on multiple selectors. For instance:
$("p").css({"border":"1px solid #000"});
Performs the subsequent function on all p tags. I've had a look through the jQuery source but to be honest it's an extensive read when you're trying to work out one specific bit of functionality. My assumption is that there's some kind of stack whereby css() and other functions merely act on the current stack, which is divined by the selector function.
Other than that, I can't work out how it could be replicated as, I think, there's no way in javascript to return multiple objects to execute a function on. E.g.
House.first_bedroom.size = "large"
House.second_bedroom.size = "small"
House.all_rooms().alertSize();
alertSize() would have to be a member function of some collection of objects rather than a member function of each room object that is returned by all_rooms()?
First, jQuery functions (generally) return a jQuery object, which acts like an array and keeps track of the current set of matched elements. Second, internally each jQuery function makes extensive use of the each() function to iterate over the matched objects, perform subsequent actions and construct the new jQuery object to return. Some functions do return something other than jQuery, like get(). These functions cannot be chained. Chaining is only possible when the function returns a jQuery object. Because the returned object is a jQuery object it has all the functions of jQuery available to it.
The jquery constructor ($(...)) always return a jquery object. You can think of it as a fancy array. The items selected are stored (looks like this is called context in the source).
So then on your object you're calling the function css... See jQuery.fn.css in the source. It basically calls a function which performs the delegate (setting or getting the css) on each item in the context.
Perhaps the DOM is parsed and all elements matching the criteria are added to an array? Or.. something more efficient? :)
Similarly, for event handling, a handler is assigned to each element in the array?
I'm just stabbing in the dark here.
I've noticed a common pattern in the JavaScript I've been writing and was wondering if there is already a pattern out there that defines something similar as best practice? Essentially, it's how to get a DOM element and wrap it inside / associate it with a JavaScript object. Take this example, where you need a filter in your web app. Your page looks like this:
<html>
<head></head>
<body>
<div id="filter"></div>
</body>
</html>
You'd then wrap the element like so:
var myFilter = new Filter({
elem: document.getElementById('filter'),
prop: 'stacks-test',
someCallback: function() {
// specify a callback
}
});
And the JavaScript (where spec is an object passed to the constructor):
var Filter = function(spec) {
this.elem = spec.elem;
this.prop = spec.prop;
this.callback = spec.someCallback;
this.bindEvents();
};
Filter.prototype.bindEvents = function() {
var self = this;
$(this.elem).click(function(e) {
self.updateFeed();
};
};
Filter.prototype.updateFeed = function() {
this.prop; // 'stacks-test'
this.callback();
// ...
// code to interact with other JavaScript objects
// who in turn, update the document
};
What is this kind of approach called, and what are the best practices and caveats?
You might be interested in Dojo's widget library, Dijit - if I'm understanding your question correctly, it essentially does what you're asking, and a whole lot more.
In Dijit, a widget essentially encapsulates a DOM node, its contents, any JavaScript that defines its behavior, and (imported separately) CSS to style its appearance.
Widgets have their own lifecycle, registry, and events (including many which simply map to DOM events on a node within the widget, e.g. myWidget.onClick could effectively call myWidget.domNode.onclick).
Widgets can (but don't have to) have their initial contents defined in a separate HTML template file, through which it's also possible to bind events on nodes within the template to widget methods, as well as set properties on the widget that reference particular nodes in the template.
I'm barely scratching the surface here. If you want to read more on this, you can start with these reference pages:
http://dojotoolkit.org/reference-guide/dijit/info.html
http://dojotoolkit.org/reference-guide/dijit/_Widget.html (the base that all widgets extend)
http://dojotoolkit.org/reference-guide/dijit/_Templated.html (RE the HTML templating)
http://dojotoolkit.org/reference-guide/quickstart/writingWidgets.html (useful information when starting to write your own widgets)
http://dojotoolkit.org/reference-guide/dijit/ (for a bunch more info)
All said, I don't know what you're ultimately aiming for, and maybe this is all a bit much for your purposes (considering I'm throwing an entire other library at you), but figured it might pique your interest at least.
Continuing from my comment on the question, jQuery is a potential tool for the job, as it already provides some of the foundations for what you're after. However, having said that, it does introduce complexities of its own, and further, not all "jQuery ways" are equal. I'll suggest one way of using jQuery as your "object model", but it may or may not suit your needs.
First things first. The philosophy of jQuery is that you start everything by selecting the element first, using $(), or equivalently jQuery(). All operations conceptually begin with this. This is a slightly different way of thinking compared to creating an object that wraps an element and keeping a reference to that wrapper, but essentially this is what jQuery does for you. A call to $('#some-id') grabs the element with id of "some-id" and wraps it in a jQuery object.
One way: Write "Filter" plugins.
Replace your constructor with a initFilter() jQuery method. You can do this by modifying the jQuery prototype and using the jQuery object as your wrapper. jQuery's prototype is referenced by jQuery.fn, so:
jQuery.fn.initFilter = function (prop, callback) {
// Save prop and callback
this.data('filter-prop', prop);
this.data('filter-callback', callback);
// Bind events (makes sense to do this during init)
this.click(function () {
$(this).updateFeed();
});
};
Then do a similar thing for updateFeed():
jQuery.fn.updateFeed = function () {
this.data('filter-prop');
this.data('filter-callback')();
});
And use it like this:
$('#filter').initFilter(prop, callback);
Note that updateFeed can simply be in-lined into the click handler to prevent unnecessary pollution of the jQuery namespace. However, one advantage of using jQuery like this is that you do not need to keep a reference to the object if you need to invoke some function on it later, since jQuery ties all references to actual elements. If you'd like to call updateFeed programmatically, then:
$('#filter').updateFeed();
will then be invoked on the correct object.
Some things to consider
There are certainly downsides to this method. One is that all properties, which we've saved against the element using .data(), are shared between all jQuery functions that act on that element. I've attempted to alleviate this by prefixing the property names with "filter-", but depending on the complexity of your object(s), this may not be suitable.
Further, this exact method may not be so suitable for objects that require a lot of manipulation (i.e. objects with many functions) since all of these functions become common to all jQuery objects. There are ways to encapsulate all this which I won't go into here, but jQuery-ui does this with their widgets, and I'm experimenting with yet another alternative in a library I'm creating.
However, pulling back a bit, the only reason I suggested using jQuery in the first place is that your Filter object appears to be heavily tied to the DOM. It binds events to the DOM, it modifies the DOM based on user interaction, basically it appears to live in the DOM, so use something DOM-based, i.e. jQuery.