jQuery Like Inline Syntax Without Using jQuery? - javascript

In jQuery you can call a function like this for example:
$("id").someFunction();
Now after looking at the codebase of jQuery it looks like the object being created by using $ has its protoype modified to return the function which was added via .fn, in my application I would like the same syntax only without requiring jQuery.
Another example of this kind of behavior is some of Javascript's in-built methods such as: .replace, .toLowerCase, .split, .toString, etc. I understand some of those listed methods are on the String.prototype object and extending in-built objects is bad practice (so I hear).
How am I able to add a function to the prototype of every "String" or "Object" that gets assigned. The reason I am doing this is I am trying to create a cross-browser implementation of attaching events without having to do if statements all of the time.
So instead of needing to go: if (el.addEventListener) or if (el.attachEvent) I would like to be able to go el.bindEvent which behind the scenes would be a prototype method behind the scenes that would do all of the checking for event binding, etc.
My advanced JS knowledge when it comes to assigning prototype methods and whatnot isn't that great, so your help in understanding and correcting anything I've said is appreciated.

Extending the prototype of DOM elements is an even worse idea than extending built-in objects †. Please have a look at the article "What's wrong with extending the DOM".
What jQuery provides is a simple wrapper around the DOM API, which is ok. The problem with extending the DOM is that
they are host objects, and might behave differently than how objects are defined in the ECMAScript spec and
older IE versions don't even expose the prototype of DOM nodes.
The reason I am doing this is I am trying to create a cross-browser implementation of attaching events without having to do if statements all of the time.
This alone is no reason to extend the DOM. You can define your own function which binds event handlers and you even have to test whether addEventListener or attachEvent exists only once on page load. Such a function could look like this:
var bindEvent = (function() {
if (document.addEventListener) {
return function(element, event, handler) {
element.addEventListener(event, handler, false);
};
}
else {
return function(element, event, handler) {
element.attachEvent("on" + event, function() {
handler.call(element, window.event);
}
};
}
}());
You can put a lot of normalization into such a function. For example, when using attachEvent, this does normally not refer to the element the handler is bound to (it refers to window), unlike with addEventListener. Also, as you might know, the event object is not passed as argument to the handler in IE. Both of these issues have been solved through the line:
handler.call(element, window.event);
(the disadvantage is that you cannot simply remove the handler, since you didn't bind handler directly, but these problems can be solved as well).
You can find more information about these browser differences and more in the excellent articles at quirksmode.org.
†: Since browsers provide Object.defineProperty and hence the possibility to mark properties as non-enumerable, extending built-in objects is not as bad anymore, but if libraries start using this, the chance of name collisions, method overriding and incompatibilities gets higher.
For your own code it should be ok. DOM objects should still be a tabu.

If I understood correctly, you want to know how to add methods to the built in javascript objects, so:
myObject.prototype.myMethod = function() {
// whatever
}
so, by example:
String.prototype.splice = function() {
// splice function for every string
}

Related

is there any way to create a javascript function like element.function(options)? [duplicate]

For example for this.parentNode I would like to just write this.p or instead of
document.getElementById('someid') just write document.g('someid'). Of course that are simple examples, I just want to know what is the correct way to do it.
(I know I can use jQuery or Prototype, but I'd like to learn how it is really done in JS)
Although you can prototype on the HTMLElement in many browsers - Internet Explorer (6,7,8) is NOT one of them. AFAIK, IE9 does support this (though I haven't tested it).
For browsers that do handle it, you can do:
HTMLElement.prototype.doHello = function(thing){
console.log(this + ' says: ' + thing)
}
document.body.doHello('hello')
I would strongly suggest not attempting to do this, for a few reasons:
Browser compatibility. While it is possible in several browsers, it isn't possible in IE <= 8.
DOM elements are host objects. Host objects (i.e. those provided by the environment that aren't native JavaScript objects) have no obligation to play by the same rules as native JavaScript objects and other than specified DOM behaviour can essentially do what they like. So, even if some browsers provide an HTMLElement prototype and allow you to augment it, there's no guarantee that it will work as you expect.
Compatibility with other code in your page. If any other code in your page (such as Prototype) messes with the HTMLElement prototype, you risk naming collisions and hard-to-detect bugs.
Instead, I would suggest creating wrapper objects around DOM nodes as jQuery, YUI and other libraries do.
Kangax has written a good article on DOM extensibility, covering all these points and more.
In a word, don't. It is best not to modify objects you don't own.
This is particularly true for HTMLElement, which you cannot modify in some browsers.
This article from perfectionkills.com will probably give you some insight into how it's done, and why you shouldn't do it.
(By the way, jQuery doesn't extend DOM elements. They use DOM wrappers instead.)
This might not be what you are looking for if you want to wrap a global object like document, but you can get a similar effect with custom-elements [1] [2] to create your own HTMLElement-like nodes.
create custom-element
add method to custom-element class
you can call the method
export class CustomElementInput extends HTMLElement {
log(){
alert("log")
}
// you can even overwrite methods like so
remove(){
alert("removing this node")
super.remove()
}
}
customElements.define("custom-element-input", CustomElementInput)
// somewhere else...
// in your HTML something like:
// <custom-element-input></custom-element-input>
const el = document.querySelector("custom-element-input")
el.log() // creates alert()

Converting a native browser event object to a jQuery event object

I am assigning an event handler function to an element through the native browser onclick property:
document.getElementById('elmtid').onclick = function(event) { anotherFunction(event) };
When I'm in anotherFunction(event), I want to be able to use the event object like I would with the event object you get in jQuery through the .on() method. I want to do this because the jQuery event object has properties and methods such as .pageX, .pageY and .stopPropagation() that work across all browsers.
So my question is, after I've passed in the native browser event object into anotherFunction(), how can I turn it into a jQuery event? I tried $(event), but it didn't work.
The obvious question here is: why don't you just use jQuery .on, .bind, .click etc to assign your event handling functions? The answer: I'm building a page that has a huge table with lots of clickable things on it. Unfortunately this project requires that the page MUST render quickly in IE6 and IE7. Using .on et al in IE6 and IE7 creates DOM leaks and eats up memory very quickly (test for yourself with Drip: http://outofhanwell.com/ieleak/index.php?title=Main_Page). Setting onclick behavior via .onclick is the only option I have to render quickly in IE6 and IE7.
Too long for a comment... Because the documentation is a bit vague on this... (I'm looking at 1.7.1 in the following)
jQuery.Event(event, props):
creates a new object
sets its type property to the event's type property.
sets isDefaultPrevented by normalized calls to all the ways to check if default is prevented.
sets originalEvent to reference the event you passed in.
adds an arbitrary set of properties provided by the props object argument.
sets a timestamp.
marks object "fixed".
What you get is basically a new object with a few additional properties and a reference to the original event - no normalization other than isDefaultPrevented.
jQuery.event.fix(event):
ignores objects that have already been marked "fixed".
makes a writable copy (by way of jQuery.Event()) and normalizes the properties mentioned here.
ETA:
Actually, looking closer at the code, jQuery.event.fix() should work - in the way described by #Beetroot-Beetroot. It's all that jQuery does to create the jQuery event object in an event dispatch.
You want jQuery.event.fix.
new jQuery.Event(nativeEvent)
Stores nativeEvent as the originalEvent property.
Handles some bubbling logic.
Timestamps the event
Marks the event as "jQuery's got this"
Gives it all the bubbling/default-preventing functions.
Note at this point the event doesn't have any "eventy" properties, just originalEvent, timeStamp, and the bubbling/default-preventing functions.
jQuery.event.fix(nativeEvent)
Does all the above
Figures out specific fixes ("fix hook") it will need to apply depending on the event type
Copies over a default set of properties from nativeEvent, plus specific ones from the fix hook
Fixes cross-browser issues with the target and metaKey properties
Applies specific cross-browser fixes and normalizations for the fix hook.
Try this:
document.getElementById('elmtid').onclick = anotherFunction;
with:
function anotherFunction(evt){
evt = $.event.fix(evt || window.event);//Note need for cross-browser reference to the native event
...
}
http://jsfiddle.net/Wrzpb/

I'm overriding the original .focus() method. Is there any way for me to continue to do so, without breaking the original behavior

A few months ago i made a Javascript library for my work, and now it looks like it has a problem with the events handler, the problem is that i have a trigger events function by using the fireEvent method, that works great, and i have something like this:
["focus", "click", "blur", ...].each(function(e){
MyEvents[e] = function(fn){
if(!fn){
trigger(element, e);
} else {
addEvent(element, e, fn);
}
}
});
Of course this is just an idea, the original function is lot bigger... well, as you can notice, i created a custom function for all standards events so i just call it like "element.click(function...); and so...
The problem is that now if i do "input.focus();" it doesnt get focus, but it trigger the event, how can i do so the element get actually in focus ?? maybe removing the focus from the array ?? and if i do so, will i have to remove some other events too like submit, blur, etc??
thank you, actually the library is being tested, so this bugs need to be corrected as soon as possible.
Thank you again.
To get the element in focus - (that is, not triggering the event itself, but focus the element) you use the .focus() method.
You can't do that with the function listed above, because that only assigns events..
You just do something like this:
document.getElementById('#inputbox').focus();
yes, it's as simple as that
Of course, I have no idea how you're referencing the elements in the first place.
after clarifications in the comments
I'm going to restate your question:
"I'm overriding the original .focus() method. Is there any way for me to continue to do so, without breaking the original behavior?"
Yes :)
Here's an example - because I don't know your variables or anything, I'm creating an element on the fly for this example - it's not required:
e = document.createElement('input');
document.body.appendChild(e);
// note: I'm using .focus() just because it was easier for me to debug.. you
// just as well replace it with .blur() instead.
e.focus = function () {
HTMLInputElement.prototype.focus.apply(this, arguments);
}
e.focus();
JS Fiddle link: http://jsfiddle.net/DK8M7/
Ok, I'm not sure how many of those variables you're familiar with. I'm giving an overview:
HTMLInputElement is the name of the original object (think of it as a "class name") for all input elements
.prototype is an object referencing a static object shared across all objects that have or have not been created yet. Kind of like an origin.
.apply() is a method used to call a function from a specific context - that is, you choose it's "this" object, the latter argument is an array of it's parameters
arguments is a special javascript array accessible from all functions which includes an array of all of it's parameters.
More on the apply method:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply
Overriding all input elements' blur() methods
One more thing... If you want all your input elements to have this behavior, the most simple way is to override it's prototype actually.. so since we're on this path, this is how you would do that:
HTMLInputElement.prototype.blurCpy = HTMLInputElement.prototype.blur;
HTMLInputElement.prototype.blur = function () {
HTMLInputElement.prototype.blurCpy.apply(this, arguments);
}
Cheers..

How to add my own methods to HTMLElement object?

For example for this.parentNode I would like to just write this.p or instead of
document.getElementById('someid') just write document.g('someid'). Of course that are simple examples, I just want to know what is the correct way to do it.
(I know I can use jQuery or Prototype, but I'd like to learn how it is really done in JS)
Although you can prototype on the HTMLElement in many browsers - Internet Explorer (6,7,8) is NOT one of them. AFAIK, IE9 does support this (though I haven't tested it).
For browsers that do handle it, you can do:
HTMLElement.prototype.doHello = function(thing){
console.log(this + ' says: ' + thing)
}
document.body.doHello('hello')
I would strongly suggest not attempting to do this, for a few reasons:
Browser compatibility. While it is possible in several browsers, it isn't possible in IE <= 8.
DOM elements are host objects. Host objects (i.e. those provided by the environment that aren't native JavaScript objects) have no obligation to play by the same rules as native JavaScript objects and other than specified DOM behaviour can essentially do what they like. So, even if some browsers provide an HTMLElement prototype and allow you to augment it, there's no guarantee that it will work as you expect.
Compatibility with other code in your page. If any other code in your page (such as Prototype) messes with the HTMLElement prototype, you risk naming collisions and hard-to-detect bugs.
Instead, I would suggest creating wrapper objects around DOM nodes as jQuery, YUI and other libraries do.
Kangax has written a good article on DOM extensibility, covering all these points and more.
In a word, don't. It is best not to modify objects you don't own.
This is particularly true for HTMLElement, which you cannot modify in some browsers.
This article from perfectionkills.com will probably give you some insight into how it's done, and why you shouldn't do it.
(By the way, jQuery doesn't extend DOM elements. They use DOM wrappers instead.)
This might not be what you are looking for if you want to wrap a global object like document, but you can get a similar effect with custom-elements [1] [2] to create your own HTMLElement-like nodes.
create custom-element
add method to custom-element class
you can call the method
export class CustomElementInput extends HTMLElement {
log(){
alert("log")
}
// you can even overwrite methods like so
remove(){
alert("removing this node")
super.remove()
}
}
customElements.define("custom-element-input", CustomElementInput)
// somewhere else...
// in your HTML something like:
// <custom-element-input></custom-element-input>
const el = document.querySelector("custom-element-input")
el.log() // creates alert()

Passing 'event' into the function as an argument

I'm novice with both JS and jQuery, and I'm a little bit confused about what situations would require you to pass event as an argument into the function, and what situations you would not need to.
For example:
$(document).ready(function() {
$('#foo').click(function() {
// Do something
});
});
versus
$(document).ready(function() {
$('#foo').click(function(event) {
// Do something
});
});
The event argument has a few uses. You only need to specify it as an argument to your handler if you're actually going to make use of it -- JavaScript handles variable numbers of arguments without complaint.
The most common use you'll see is to prevent the default behavior of the action that triggered the event. So:
$('a.fake').click(function(e) {
e.preventDefault();
alert("This is a fake link!");
});
...would stop any links with the class fake from actually going to their href when clicked. Likewise, you can cancel form submissions with it, e.g. in validation methods. This is like return false, but rather more reliable.
jQuery's event object is actually a cross-browser version of the standard event argument provided in everything but IE. It's essentially a shortcut, that lets you use only one code path instead of having to check what browser you're using in every event handler.
(If you read non-jQuery code you'll see a lot of the following, which is done to work around IE's deficiency.
function(e) {
e = e || window.event; // For IE
It's a pain, and libraries make it so much easier to deal with.)
There's a full accounting of its properties in the jQuery docs. Essentially, include it if you see anything you need there, and don't worry otherwise. I like to include it always, just so I never have to remember to add it in later if I decide that it's needed after all.
You only need the event if you're going to use it in the body of the handler.
Since you are using jQuery, you only put event as an argument if you need to use the event in the handler, such as if you need the key that was pressed on a keypress event.
In JS, without jQuery or Prototype etc., you need to pass the event as a parameter for standards compliant browsers like Firefox, but the event is not passed as an argument in IE. IE actually maintains a global variable window.event. So in your handler (sans library) you need to check if the argument is undefined; if so, grab the global variable.
function eventHandler(evt) {
var theEvent = evt || window.event;
//use the event
}
But a library like jQuery takes care of that for you.
I honestly don't recommend using a library until you have learned the language. But if this is for a job, the by all means use the library, but learn the details of JS on your own, so you can better appreciate it.

Categories

Resources