Documentos de Académico
Documentos de Profesional
Documentos de Cultura
com/you-dont-need-jquery/ajax/
https://github.com/github/fetch
1.
2.
3.
4.
5.
6.
7.
8.
9.
Ajax Requests
Many developers who learned web development through a
jQuery lens probably think that jQuery is doing something
magical when you invoke the $.ajax method. That couldn't
be further from the truth. All of the heavy lifting is done by
the browser via the XMLHttpRequest object. jQuery's ajax
is just a wrapper around XMLHttpRequest. Using the
browser's built-in support for ajax requests isn't very
difficult, as you'll see in a moment. Even cross-origin
requests are simple.
GETting
POSTing
URL Encoding
JSON
Uploading
CORS
JSONP
Libraries to Consider
Next in this Series
GETting
Let's start with a simple but common request. We need to
ask the server for the name of a person, given that
person's unique ID. The unique ID string should be
included as a query parameter in the URI, with an empty
payload, as is common for GET requests. Invoke an alert
with the user's name, or an error if the request fails.
jQuery
There are a couple ways to initiate a GET ajax request
using jQuery's API. One involves the get method, which is
shorthand for ajax with a type of 'get'. We'll just use the
ajax method going forward for consistency.
$.ajax('myservice/username', {
data: {
id: 'some-unique-id'
}
})
.then(
function success(name) {
alert('User\'s name is ' + name);
},
function fail(data, status) {
alert('Request failed. Returned status of ' + status);
}
);
Native XMLHttpRequest Object
var xhr = new XMLHttpRequest();
xhr.open('GET', 'myservice/username?id=some-unique-id');
xhr.onload = function() {
if (xhr.status === 200) {
alert('User\'s name is ' + xhr.responseText);
}
else {
alert('Request failed. Returned status of ' +
xhr.status);
}
};
xhr.send();
The above native JS example will work in IE7 and up. Even
IE6 is trivial to support, just by swapping out new
XMLHttpRequest() with new
data: file
});
That's a bit better, but we still need to include the nonsensical processData: false option to prevent jQuery from
attempting to URL-encode the payload.
XMLHttpRequest
First, multipart encoded:
var formData = new FormData(),
file = document.getElementById('test-input').files[0],
xhr = new XMLHttpRequest();
formData.append('file', file);
xhr.open('POST', 'myserver/uploads');
xhr.send(formData);
And now, let's send the file as the payload of the request:
var file = document.getElementById('test-input').files[0],
xhr = new XMLHttpRequest();
xhr.open('POST', 'myserver/uploads');
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);
Hey, that was really easy. All the power in uploading files
comes from the File API and XMLHttpRequest. jQuery just
gets in the way.
CORS
CORS, or Cross Origin Resource Sharing (sending crossdomain ajax requests) is actually a fairly complex topic,
and there is much to discuss here. But, we're really not
concerned with all the details here. This assumes you
already understand CORS and the Same Origin Policy. If
you don't, MDN has a great explanation. Maybe I'll even
take some time to write more on the topic.
Anyway, sending a cross-origin ajax request via JavaScript
is pretty straightforward in modern browsers. The process
is a bit hairy in IE8 and IE9 though. In either case, jQuery
offers zero assistance.
For modern browsers, all of the work is delegated to the
server. The browser does everything else for you. Your
code for a cross-origin ajax request in a modern browser is
identical to a same-origin ajax request. So, I won't bother
showing that in jQuery or native JavaScript.
It's important to know that cookies are not sent by default
with cross-origin ajax requests. You must set the
withCredentials flag on the XMLHttpRequest transport.
Let's take a look.
jQuery
$.ajax('http://someotherdomain.com', {
method: 'POST',
contentType: 'text/plain',
data: 'sometext',
beforeSend: function(xmlHttpRequest) {
xmlHttpRequest.withCredentials = true;
}
});
XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://someotherdomain.com');
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.send('sometext');
Clearly no benefit from jQuery here.
jQuery actually becomes a headache to deal with when we
need to send a cross-domain ajax request in IE8 or IE9. If
you're using jQuery for this purpose, you are truly trying to
fit a square peg into a round hole.
To understand why jQuery is a poor fit for cross-origin
requests in IE9 and IE8, it's important to understand a
couple low-level points:
1.
});
jQuery has entirely abstracted away the awfulness of
JSONP. +1 for jQuery here. But, we can still accomplish all
of this without jQuery, and it's not as complicated as it
might seem:
Without jQuery
window.myJsonpCallback = function(data) {
// handle requested data from server
};
var scriptEl = document.createElement('script');
scriptEl.setAttribute('src',
'http://jsonp-aware-endpoint.com/user?
callback=myJsonpCallback&id=123');
document.body.appendChild(scriptEl);
Libraries to Consider
I beleive that the examples I provided above show that any
ajax related code can be done fairly easily without pulling
in any dependencies. But, if you're not convinced and don't
want to pull in jQuery just for some ajax help, there are a
few focused libraries you can check out.
1.
fetch: a polyfill for the emerging fetch standard,
which aims to make native ajax code more intuitive and
modern.
2.
xdomain: A library that makes cross-origin requests
in all browsers, back to IE8, really easy. It makes use of the
Web Messaging API, and includes some of its own
conventions to make this work. The server buy-in is must
simpler than the requirements for CORS as well due to
some clever workarounds in this library.
3.
Lightweight-JSONP: As the name suggests, this is
a small library that aims to make JSONP a breeze in the
browser.
Next
For me: I'll talk about dealing with events (both DOM/native
and custom).
For you: if I've left out any important ajax-related topics, let
me know in the comments so I can update the post.
Written on December 14, 2014
No necesita jQuery!
GETting
POSTing
URL Encoding
JSON
Uploading
CORS
JSONP
GETting
POSTing
URL Encoding
JSON
Uploading
CORS
Libraries to Consider
Next in this Series
JSONP
Libraries to Consider
Next in this Series
GETting
GETting
Let's start with a simple but common request. We need to
ask the server for the name of a person, given that
person's unique ID. The unique ID string should be
included as a query parameter in the URI, with an empty
payload, as is common for GET requests. Invoke an alert
with the user's name, or an error if the request fails.
jQuery
jQuery
$.ajax('myservice/username', {
data: {
id: 'some-unique-id'
}
})
.then(
function success(name) {
alert('User\'s name is ' + name);
},
It seems pretty clear here that the jQuery way to send this
request is much more elegant. It does a portion of the work
for you. But, is this elegance worth pulling in the
dependency? If you are comfortable enough with the
relatively simple XMLHttpRequest object, the answer is
probably "no".
URL Encoding
jQuery provides a function that takes an object and turns it
into a URL encoded string:
$.param({
key1: 'some value',
'key 2': 'another value'
});
The above code will work in IE8 and up. But maybe you
work at an awful company that requires support for ancient
browsers. In that case, just drop in json.js to fill in for the
lack of JSON support in IE7 and older.
Uploading Files
For starters, you should know that the only way to upload
files in IE9 and older is by submitting a <form> that
contains an <input type="file">. jQuery isn't going to help
you out much with that, and frankly neither is the Web API.
jQuery
First, we'll upload a file as part of a multipart encoded
request:
var file = $('#test-input')[0].files[0],
formData = new FormData();
formData.append('file', file);
$.ajax('myserver/uploads', {
method: 'POST',
contentType: false,
processData: false,
data: formData
});
How non-intuitive is that? contentType: false? What does
that even mean? Well, this is required to ensure that
jQuery doesn't insert its own Content-Type header, since
the browser MUST specify the Content-Type for you as it
includes a calculated multipart boundary ID used by the
server to parse the request.
Now, let's send a POST where the entire payload of the
request consists of the file data:
And now, let's send the file as the payload of the request:
Hey, that was really easy. All the power in uploading files
comes from the File API and XMLHttpRequest. jQuery just
gets in the way.
CORS
CORS, or Cross Origin Resource Sharing (sending crossdomain ajax requests) is actually a fairly complex topic,
and there is much to discuss here. But, we're really not
concerned with all the details here. This assumes you
already understand CORS and the Same Origin Policy. If
you don't, MDN has a great explanation. Maybe I'll even
take some time to write more on the topic.
Anyway, sending a cross-origin ajax request via JavaScript
is pretty straightforward in modern browsers. The process
is a bit hairy in IE8 and IE9 though. In either case, jQuery
offers zero assistance.
For modern browsers, all of the work is delegated to the
server. The browser does everything else for you. Your
code for a cross-origin ajax request in a modern browser is
identical to a same-origin ajax request. So, I won't bother
showing that in jQuery or native JavaScript.
It's important to know that cookies are not sent by default
with cross-origin ajax requests. You must set the
withCredentials flag on the XMLHttpRequest transport.
Let's take a look.
jQuery
$.ajax('http://someotherdomain.com', {
method: 'POST',
contentType: 'text/plain',
data: 'sometext',
beforeSend: function(xmlHttpRequest) {
xmlHttpRequest.withCredentials = true;
}
});
Clearly no benefit from jQuery here.
jQuery actually becomes a headache to deal with when we
need to send a cross-domain ajax request in IE8 or IE9. If
you're using jQuery for this purpose, you are truly trying to
fit a square peg into a round hole.
To understand why jQuery is a poor fit for cross-origin
requests in IE9 and IE8, it's important to understand a
couple low-level points:
Cross-origin ajax requests in IE8 and IE9 can only be sent
using the IE-proprietary XDomainRequest transport. I'll
save the rant for why this was such a huge mistake by the
IE development team for another blog post. Regardless,
XDomainRequest is a stripped down version of
XMLHttpReqest, and it must be used when making crossorigin ajax requests in IE8 and IE9. To read more about the
(significant) restrictions imposed on this transport, read
Eric Law's MSDN post on the subject.
Note that you cannot set any request headers when using
XDomainRequest. If you can avoid making cross-origin
ajax requests in IE8/9, you should. But if you must,
become familiar with its limitations.
JSONP
I'll begin here by suggesting you avoid using JSONP, as it's
proven to be a potential security issue. Also, in modern
browsers, CORS is a much better route.
If you're not familiar with JSONP, the name may be a bit
misleading. There is actually no JSON involved here at all.
It's a very common misconception that JSON must be
returned from the server when the client initiates a JSONP
call, but that's simply not true. Instead, the server returns a
function invocation, which is not valid JSON.
JSONP stands for JavaScript Object Notation with
Padding. It's essentially just an ugly hack that exploits the
fact that <script> tags that load content from a server are
not bound by the same-origin policy. There needs to be
cooperation and an understanding of the convention by
both client and server for this to work properly. You simply
need to point the src attribute of a <script> tag at a
JSONP-aware endpoint, including the name of an exisitng
global function as a query parameter. The server will then
construct a string representation that, when executed by
the browser, will invoke the global function, passing in the
requested data.
jQuery
$.ajax('http://jsonp-aware-endpoint.com/user', {
jsonp: 'callback',
dataType: 'jsonp',
data: {
id: 123
}
}).then(function(response) {
// handle requested data from server
});
jQuery has entirely abstracted away the awfulness of
JSONP. +1 for jQuery here. But, we can still accomplish all
of this without jQuery, and it's not as complicated as it
might seem:
Without jQuery
1.
2.
3.
Libraries to Consider
I beleive that the examples I provided above show that any
ajax related code can be done fairly easily without pulling
in any dependencies. But, if you're not convinced and don't
want to pull in jQuery just for some ajax help, there are a
few focused libraries you can check out.
fetch: a polyfill for the emerging fetch standard,
which aims to make native ajax code more intuitive and
modern.
xdomain: A library that makes cross-origin requests
in all browsers, back to IE8, really easy. It makes use of the
Web Messaging API, and includes some of its own
conventions to make this work. The server buy-in is must
simpler than the requirements for CORS as well due to
some clever workarounds in this library.
Lightweight-JSONP: As the name suggests, this is
a small library that aims to make JSONP a breeze in the
browser.
Next
For me: I'll talk about dealing with events (both DOM/native
and custom).
For you: if I've left out any important ajax-related topics, let
me know in the comments so I can update the post.
Written on December 14, 2014
Events
In this fifth installment of "You Don't Need jQuery
(anymore)", I'm going to talk about dealing with events in
the browser without jQuery. As always, each section will
cover the jQuery approach, followed by a solution using the
native Web API instead. After reading this post on events,
you should be confident enough to deal with events in your
own project without using jQuery.
Events
1.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
someElement.removeEventListener('click',
myEventHandler);
Modifying Events
The ability to modify an event generally refers to
augmenting or squelching the event by one event handler
before it reaches other event handlers.
Preventing the event from bubbling further up the DOM
Here, we want to ensure the event, caught by our event
handler, will not reach any additional event handlers
positioned on ancestor elements. This will stop the event
from bubbling up the DOM.
jQuery
$(someEl).on('some-event', function(event) {
event.stopPropagation();
});
Web API
someEl.addEventListener('some-event', function(event) {
event.stopPropagation();
});
The syntax between jQuery and the modern Web API here
is almost identical.
Preventing the event from hitting any additional handlers
attached to the current element
Not only do we want to prevent this event from hitting any
handlers bound to elements that are ancestors of this one,
but we also want to ensure no other event handlers bound
to this element are hit either. So, we want to prevent the
event from hitting any further event handlers.
jQuery
$(someEl).on('some-event', function(event) {
event.stopImmediatePropagation();
});
Web API
someEl.addEventListener('some-event', function(event) {
event.stopImmediatePropagation();
});
Again, the syntax between jQuery and the modern Web
API here is eerily similar.
Preventing the event from triggering an action defined by
the browser
Let's say we have the following element:
<a href="http://fineuploader.com">Go to Fine Uploader</a>
...and we want to prevent a click on this anchor from
opening the associated page. This will involve adding a
click handler to the anchor element and instructing the
browser to not execute the its native/default action.
jQuery
$(someAnchor).on('click', function(event) {
event.preventDefault();
});
Web API
});
Remember, this isn't about elegant code, it's about
exploring and understanding the Web API. After all, jQuery
is just a Web API wrapper.
Keyboard Events
In this section, I'll show how to handle various keyboard
events. I'll also show how to identify which specific key was
pressed by the user.
First, we should take a moment to be sure we understand
the difference between the three different types of general
keyboard events:
1.
keydown: Key has been pressed but not yet
released. No default action has been performed yet.
2.
keypress: Key has been pressed and a character
has registered. This event will fire continuously if the key is
held down.
3.
keyup: Pressed key has been released.
Let's say we are building a web application and want to
make an interactive tutorial that we've build accessible via
an intuitive keyboard shortcut. Anywhere in the application,
our users should be able to pull up our help widget via the
Ctrl-H key combination.
jQuery
$(document).keydown(function(event) {
if (event.ctrlKey && event.which === 72) {
// open help widget
}
});
Web API
document.addEventListener('keydown', function(event) {
if (event.ctrlKey && event.which === 72) {
// open help widget
}
});
Nothing is obviously gained, in this instance, with jQuery.
Even the syntax is almost identical between the Web API
and jQuery.
Registering for other keyboard events follows a similar
pattern:
jQuery
$(someElement).keypress(function(event) {
// ...
});
$(someElement).keyup(function(event) {
// ...
});
Web API
someElement.addEventListener('keypress', function(event)
{
// ...
});
someElement.addEventListener('keyup', function(event) {
// ...
});
For more information about keyboard event properties and
browser compatibility among the properties of this event,
have a look at the KeyboardEvent document on Mozilla
Developer Network.
Mouse Events
There are a number of mouse events provided by the Web
API, such as "mousedown", "mouseenter", and
"mouseover" (to name a few). It isn't particularly interesting
to show how to register for and handle these events in
jQuery and the Web API. Much like keyboard events, the
syntax between the two is almost identical.
Instead, I'm going to focus on one special event that is part
someAnchor.addEventListener('click', function(event) {
event.preventDefault();
});
Are you seeing a pattern here? The syntax required to deal
with events is starting to look about the same between
jQuery and the Web API.
Event Delegation
Suppose you have a list filled with list items that are
sensitive to mouse clicks. You could attach a click handler
to each individual list item. However, this may be inefficient
and slow down your page with a large number of list items.
Suppose items are added to this list dynamically. Now
attaching a new event handler to each new list item, as it is
added, becomes less appealing.
The solution here is event delegation. That is, attach one
click handler to the list. When any of the list items are
clicked, the event will bubble up to its parent, the list
container element. At this point, your one event handler will
be hit and you can easily determine, by inspecting the
event object, which list item was clicked and respond
appropriately.
The markup for such a list may look like this:
<ul id="my-list">
<li>foo <button>x</button></li>
<li>bar <button>x</button></li>
<li>abc <button>x</button></li>
<li>123 <button>x</button></li>
</ul>
If the user clicks on the "x" button, the list item should be
removed from the list.
jQuery
jQuery will set the context of our handler to the element
that initially received the click (the <button>). Also, only
<button> elements inside this list will be examined.
$('#my-list').on('click', 'BUTTON', function() {
$(this).parent().remove();
});
Web API
We have to write a couple more lines of code without
jQuery, but this still isn't exactly rocket science. The Web
API always sets the context of the event handler to the
element receiving the click event, which in this case is the
list container element. Instead, we need to know which
element was initially clicked, which is available in the target
property of the provided Event object. We must also ensure
that we only act on <button> clicks.
document.getElementById('mylist').addEventListener('click', function(event) {
var clickedEl = event.target;
if(clickedEl.tagName === 'BUTTON') {
var listItem = clickedEl.parentNode;
listItem.parentNode.removeChild(listItem);
}
});
Remember, this isn't about elegant code, it's about
function hoverOut() {
// mouse was hovering over this element, but no
longer is
}
);
We can do the same thing with the Web API pretty easily:
someEl.addEventListener('mouseover', function() {
// mouse is hovering over this element
});
someEl.addEventListener('mouseout', function() {
// mouse was hovering over this element, but no longer
is
});
As I mentioned earlier, mouse events are fairly
straightforward to handler with the Web API. For more
information on mouse events, have a look at the
MouseEvent interface documentation on the Mozilla
Developer Network.
Browser Load Events
When talking about "load" events in the browser, we may
be trying to answer any one of the following questions:
When have all elements on the page fully loaded and
rendered w/ applied styles?
This event should be fired after:
all markup has been placed on the page
all stylesheets have been loaded
all images have loaded
all iframes have fully loaded
jQuery
$(window).load(function() {
// page is fully rendered
})
Web API
window.addEventListener('load', function() {
// page is fully rendered
});
When has all static markup been placed on the page?
This event should fire after all markup has been placed on
the page.
jQuery
$(document).ready(function() {
// markup is on the page
});
Note that you can also achieve the same thing in jQuery
this way:
$(function() {
// markup is on the page
});
Web API
document.addEventListener('DOMContentLoaded',
function() {
// markup is on the page
});
Note that you will likely want to ensure your script that
}
// example use
registerHandler(someElement, 'click', function() {
$(window).load(function() {
// page is fully rendered
})
Web API
window.addEventListener('load', function() {
// page is fully rendered
});
When has all static markup been placed on the page?
This event should fire after all markup has been placed on
the page.
jQuery
removeMethod(eventName, callback);
}
// example use
unregisterHandler(someElement, 'click',
someEventHandlerFunction);
Form Field Change Events
Older versions of IE have some serious change-event
deficiencies. Here are the two big ones that come to mind:
1.
Change events do not bubble.
2.
Checkboxes and radio buttons may not trigger a
change event at all.
It's important to know that the 2nd issue above was also
reproducible when using jQuery with IE7/8 for quite a long
time. As far as I can tell, current versions of jQuery
(~1.10+) do properly address this issue though.
To solve issue #1, you must attach a change handler
directly to any form field that you'd like to monitor. Event
delegation is not possible. For issue #2, you're best bet
may be to attach a click handler to radio/checkbox fields
instead of relying on the change event.
The Event Object
Some properties of the Event object instance are a bit
different in older browsers. For example, while the target of
the event in modern browsers can be found by checking
the target property of the Event instance, IE8 and older
contain a different property for this element, named
srcElement. So, now your cross-browser event handler
looks like this:
function myEventHandler(event) {
var target = event.target || event.srcElement
// handle event & target
}
In terms of controlling your events, the stopPropagation
method is not available on an Event object instance in IE8
and older. If you want to stop an event from bubbling, you
must instead set the cancelBubble property on the event. A
cross-browser solution would look like this:
function myEventHandler(event) {
if (event.stopPropgation) {
event.stopPropagation();
}
else {
event.cancelBubble = true;
}
}
IE8 and older also do not have a
stopImmediatePropagation method. There isn't much to be
done about this. I don't see lack of this method as a big
loss, as use of stopImmediatePropagation seems like a
code smell to me since the behavior of this call is
completely dependent on the order that multiple event
handlers are attached to the element in question.
Browser Load Events
While load events on the window seem to function fairly
$(document).ready(function() {
// markup is on the page
});
Note that you can also achieve the same thing in jQuery
this way:
$(function() {
// markup is on the page
});
Web API
document.addEventListener('DOMContentLoaded',
function() {
// markup is on the page
});
Note that you will likely want to ensure your script that
registers the DOMContentLoaded event handler is placed
before any stylesheet <link> tags, since loading these
stylsheets will block any script execution and prolong the
DOMContentLoaded event until the defined stylesheets
have loaded.
When has a particular element on the page fully loaded?
In addition to window, load events are associated with a
number of elements, such as <img>, <link>, and <script>.
The most common use of this event outside of window is to
determine when a specific image has loaded.
jQuery
$('img').load(function() {
// image has successfully loaded
});
You can also determine if the image has failed to load:
$('img').error(function() {
// image has failed to load
});
Web API
img.addEventListener('load', function() {
// image has successfully loaded
});
And if the image should fail to load?
img.addEventListener('error', function() {
// image has failed to load
});
As we've seen many times before, the syntax between