diff --git a/06_object.md b/06_object.md index 9af33b4e3..3f5f09959 100644 --- a/06_object.md +++ b/06_object.md @@ -664,6 +664,8 @@ console.log(okIterator.next()); {{index "matrix example", "Matrix class", array}} +{{id matrix}} + Let's implement an iterable data structure. We'll build a _matrix_ class, acting as a two-dimensional array. diff --git a/18_http.md b/18_http.md index 670ba1c06..493e22350 100644 --- a/18_http.md +++ b/18_http.md @@ -1,4 +1,4 @@ -{{meta {load_files: ["code/chapter/17_http.js"]}}} +{{meta {load_files: ["code/chapter/18_http.js"]}}} # HTTP and Forms @@ -12,12 +12,12 @@ JavaScript has access to it. {{index "IP address"}} -If you type _eloquentjavascript.net/17_http.html_ into -your browser's ((address bar)), the ((browser)) first looks up the -((address)) of the server associated with _eloquentjavascript.net_ -and tries to open a ((TCP)) ((connection)) to it on ((port)) 80, the -default port for ((HTTP)) traffic. If the ((server)) exists and -accepts the connection, the browser sends something like this: +If you type _eloquentjavascript.net/18_http.html_ into your browser's +((address bar)), the ((browser)) first looks up the ((address)) of the +server associated with _eloquentjavascript.net_ and tries to open a +((TCP)) ((connection)) to it on ((port)) 80, the default port for +((HTTP)) traffic. If the ((server)) exists and accepts the connection, +the browser might send something like this: ```{lang: http} GET /17_http.html HTTP/1.1 @@ -31,19 +31,20 @@ Then the server responds, through that same connection. HTTP/1.1 200 OK Content-Length: 65585 Content-Type: text/html -Last-Modified: Wed, 09 Apr 2014 10:48:09 GMT +Last-Modified: Mon, 08 Jan 2018 10:29:45 GMT ... the rest of the document ``` -The browser then takes the part of the ((response)) after the blank -line and displays it as an ((HTML)) document. +The browser takes the part of the ((response)) after the blank line, +its _body_ (not to be confused with the HTML `` tag), and +displays it as an ((HTML)) document. {{index HTTP}} -The information sent by the client is called the -_((request))_. It starts with this line: +The information sent by the client is called the _((request))_. It +starts with this line: ```{lang: http} GET /17_http.html HTTP/1.1 @@ -51,35 +52,42 @@ GET /17_http.html HTTP/1.1 {{index "DELETE method", "PUT method", "GET method"}} -The first word is -the _((method))_ of the ((request)). `GET` means that we want to _get_ -the specified resource. Other common methods are `DELETE` to delete a -resource, `PUT` to replace it, and `POST` to send information to it. -Note that the ((server)) is not obliged to carry out every request it -gets. If you walk up to a random website and tell it to `DELETE` its -main page, it'll probably refuse. - -{{index [path, URL], Twitter}} - -The part after the ((method)) name is the path of the -((resource)) the request applies to. In the simplest case, a resource -is simply a ((file)) on the ((server)), but the protocol doesn't -require it to be. A resource may be anything that can be transferred _as if_ -it is a file. Many servers generate the responses they produce on the -fly. For example, if you open -http://twitter.com/marijnjh[_twitter.com/marijnjh_], the server looks -in its database for a user named _marijnjh_, and if it finds one, it +The first word is the _((method))_ of the ((request)). `GET` means +that we want to _get_ the specified resource. Other common methods are +`DELETE` to delete a resource, `PUT` to replace it, and `POST` to send +information to it. Note that the ((server)) is not obliged to carry +out every request it gets. If you walk up to a random website and tell +it to `DELETE` its main page, it'll probably refuse. + +{{index [path, URL], GitHub}} + +The part after the ((method)) name is the path of the ((resource)) the +request applies to. In the simplest case, a resource is simply a +((file)) on the ((server)), but the protocol doesn't require it to be. +A resource may be anything that can be transferred _as if_ it is a +file. Many servers generate the responses they produce on the fly. For +example, if you open +[_github.com/marijnh_](https://github.com/marijnh), the server looks +in its database for a user named "marijnh", and if it finds one, it will generate a profile page for that user. After the resource path, the first line of the request mentions -`HTTP/1.1` to indicate the ((version)) of the ((HTTP)) ((protocol)) -it is using. +`HTTP/1.1` to indicate the ((version)) of the ((HTTP)) ((protocol)) it +is using. + +In practice, many sites use HTTP version 2, which supports the same +concepts as version 1.1, but is, for performance reasons, a lot more +complicated. Browsers will automatically switch to the appropriate +protocol version when talking to a given server, and the outcome of a +request is the same regardless which version is used. Because version +1.1 is more straightforward and easier to play around with, we'll +focus on that. {{index "status code"}} -The server's ((response)) will start with a version -as well, followed by the status of the response, first as a -three-digit status code and then as a human-readable string. +The server's ((response)) will start with a version as well, followed +by the status of the response, first as a three-digit status code and +then as a human-readable string. ```{lang: http} HTTP/1.1 200 OK @@ -90,71 +98,66 @@ HTTP/1.1 200 OK Status codes starting with a 2 indicate that the request succeeded. Codes starting with 4 mean there was something wrong with the ((request)). 404 is probably the most famous HTTP status code—it means -that the resource that was requested could not be found. Codes that -start with 5 mean an error happened on the ((server)) and the request -is not to blame. +that the resource could not be found. Codes that start with 5 mean an +error happened on the ((server)) and the request is not to blame. {{index HTTP}} {{id headers}} -The first line of a request or response may be followed by -any number of _((header))s_. These are lines in the form “name: value” -that specify extra information about the request or response. These -headers were part of the example ((response)): + +The first line of a request or response may be followed by any number +of _((header))s_. These are lines in the form `name: value` that +specify extra information about the request or response. These headers +were part of the example ((response)): ```{lang: null} Content-Length: 65585 Content-Type: text/html -Last-Modified: Wed, 09 Apr 2014 10:48:09 GMT +Last-Modified: Thu, 04 Jan 2018 14:05:30 GMT ``` {{index "Content-Length header", "Content-Type header", "Last-Modified header"}} -This tells us the size and type of the response document. In -this case, it is an HTML document of 65,585 bytes. It also tells us when +This tells us the size and type of the response document. In this +case, it is an HTML document of 65,585 bytes. It also tells us when that document was last modified. {{index "Host header", domain}} -For the most part, a client or server -decides which ((header))s to include in a ((request)) or ((response)), -though a few headers are required. For example, the `Host` header, -which specifies the hostname, should be included in a request -because a ((server)) might be serving multiple hostnames on a single -((IP address)), and without that header, the server won't know which host the -client is trying to talk to. +For the most part, a client or server decides which ((header))s to +include in a ((request)) or ((response)), though a few headers are +required. For example, the `Host` header, which specifies the +hostname, should be included in a request because a ((server)) might +be serving multiple hostnames on a single ((IP address)), and without +that header, the server won't know which host the client is trying to +talk to. {{index "GET method", "DELETE method", "PUT method", "POST method", "body (HTTP)"}} -After the headers, both requests and -responses may include a blank line followed by a _body_, which -contains the data being sent. `GET` and `DELETE` requests don't send -along any data, but `PUT` and `POST` requests do. -Similarly, some response types, such as error responses, do not -require a body. +After the headers, both requests and responses may include a blank +line followed by the body, which contains the data being sent. `GET` +and `DELETE` requests don't send along any data, but `PUT` and `POST` +requests do. Similarly, some response types, such as error responses, +do not require a body. ## Browsers and HTTP {{index HTTP}} -As we saw in the example, a ((browser)) will make a request -when we enter a ((URL)) in its ((address bar)). When the resulting -HTML page references other files, such as ((image))s and JavaScript -((file))s, those are also fetched. +As we saw in the example, a ((browser)) will make a request when we +enter a ((URL)) in its ((address bar)). When the resulting HTML page +references other files, such as ((image))s and JavaScript ((file))s, +those are also retrieved. {{index parallelism, "GET method"}} -A moderately complicated ((website)) can easily -include anywhere from 10 to 200 ((resource))s. To be able to -fetch those quickly, browsers will make several requests -simultaneously, rather than waiting for the responses one at a time. -Such documents are always fetched using `GET` -((request))s. +A moderately complicated ((website)) can easily include anywhere from +10 to 200 ((resource))s. To be able to fetch those quickly, browsers +will make several `GET` requests simultaneously, rather than waiting +for the responses one at a time. -{{id http_forms}} -HTML pages may include _((form))s_, which allow -the user to fill out information and send it to the server. This is an -example of a form: +HTML pages may include _((form))s_, which allow the user to fill out +information and send it to the server. This is an example of a form: ```{lang: "text/html"}
@@ -166,51 +169,53 @@ example of a form: {{index form, "method attribute", "GET method"}} -This code describes a form with two -((field))s: a small one asking for a name and a larger one to write a -message in. When you click the Send ((button)), the information in -those fields will be encoded into a _((query string))_. When the -`` element's `method` attribute is `GET` (or is omitted), that -query string is tacked onto the `action` URL, and the browser makes a -`GET` request to that URL. +This code describes a form with two ((field))s: a small one asking for +a name and a larger one to write a message in. When you click the Send +((button)), the form is _submitted_, meaning that the content of its +field is packed into an HTTP request and the browser navigates to the +result of that request. -```{lang: "text/html"} +When the `` element's `method` attribute is `GET` (or is +omitted), the information in the form is added to the end of the +`action` URL as a _((query string))_. The browser might then make a +request to this URL: + +```{lang: null} GET /example/message.html?name=Jean&message=Yes%3F HTTP/1.1 ``` {{index "ampersand character"}} -The start of a ((query string)) is indicated -by a ((question mark)). After that follow pairs of names and values, -corresponding to the `name` attribute on the form field elements and -the content of those elements, respectively. An ampersand character (`&`) is used to separate -the pairs. - -{{index [escaping, "in URLs"], "hexadecimal number", "percent sign", "URL encoding", "encodeURIComponent function", "decodeURIComponent function"}} - -The actual message encoded -in the previous URL is “Yes?”, even though the question mark is replaced -by a strange code. Some characters in query strings must be -escaped. The question mark, represented as `%3F`, is one of those. -There seems to be an unwritten rule that every format needs its -own way of escaping characters. This one, called _URL -encoding_, uses a percent sign followed by two hexadecimal digits -that encode the character code. In this case, 3F, which is 63 in -decimal notation, is the code of a question mark character. JavaScript -provides the `encodeURIComponent` and `decodeURIComponent` functions -to encode and decode this format. +The ((question mark)) indicates the end of the path part of the URL +and the start of the query. After that follow pairs of names and +values, corresponding to the `name` attribute on the form field +elements and the content of those elements, respectively. An ampersand +character (`&`) is used to separate the pairs. + +{{index [escaping, "in URLs"], "hexadecimal number", "encodeURIComponent function", "decodeURIComponent function"}} + +The actual message encoded in the URL is "Yes?", but the question mark +is replaced by a strange code. Some characters in query strings must +be escaped. The question mark, represented as `%3F`, is one of those. +There seems to be an unwritten rule that every format needs its own +way of escaping characters. This one, called _((URL encoding))_, uses +a ((percent sign)) followed by two hexadecimal digits that encode the +character code. In this case, 3F, which is 63 in decimal notation, is +the code of a question mark character. JavaScript provides the +`encodeURIComponent` and `decodeURIComponent` functions to encode and +decode this format. ``` -console.log(encodeURIComponent("Hello & goodbye")); -// → Hello%20%26%20goodbye -console.log(decodeURIComponent("Hello%20%26%20goodbye")); -// → Hello & goodbye +console.log(encodeURIComponent("Yes?")); +// → Yes%3F +console.log(decodeURIComponent("Yes%3F")); +// → Yes? ``` {{index "body (HTTP)", "POST method"}} -If we change the `method` attribute -of the HTML form in the example we saw earlier to `POST`, the ((HTTP)) request made to submit the +If we change the `method` attribute of the HTML form in the example we +saw earlier to `POST`, the ((HTTP)) request made to submit the ((form)) will use the `POST` method and put the ((query string)) in body of the request, rather than adding it to the URL. @@ -222,426 +227,168 @@ Content-type: application/x-www-form-urlencoded name=Jean&message=Yes%3F ``` -By convention, the `GET` method is used for requests that do not have -side effects, such as doing a search. Requests that change something on -the server, such as creating a new account or posting a message, should -be expressed with other methods, such as `POST`. Client-side software, -such as a browser, knows that it shouldn't blindly make `POST` -requests but will often implicitly make `GET` requests—for example, to -prefetch a resource it believes the user will soon need. - -The [next chapter](forms) will return to forms -and talk about how we can script them with JavaScript. - -{{id xmlhttprequest}} -## XMLHttpRequest - -{{index capitalization, XMLHttpRequest}} - -The ((interface)) through -which browser JavaScript can make HTTP requests is called -`XMLHttpRequest` (note the inconsistent capitalization). It was -designed by ((Microsoft)), for its ((Internet Explorer)) -((browser)), in the late 1990s. During this time, the ((XML)) file format -was _very_ popular in the world of ((business software))—a world where -Microsoft has always been at home. In fact, it was so popular that the -acronym XML was tacked onto the front of the name of an interface for -((HTTP)), which is in no way tied to XML. - -{{index modularity, [interface, design]}} - -The name isn't completely -nonsensical, though. The interface allows you to parse response documents as -XML if you want. Conflating two distinct concepts (making a request -and ((parsing)) the response) into a single thing is terrible design, -of course, but so it goes. - -When the `XMLHttpRequest` interface was added to Internet Explorer, it -allowed people to do things with JavaScript that had been very hard -before. For example, websites started showing lists of suggestions -when the user was typing something into a text field. The script would -send the text to the server over ((HTTP)) as the user typed. The ((server)), -which had some ((database)) of possible inputs, would -match the database entries against the partial input and send back possible -((completion))s to show the user. This was -considered spectacular—people were used to waiting for a full page reload -for every interaction with a website. - -{{index compatibility, Firefox, XMLHttpRequest}} - -The other -significant browser at that time, ((Mozilla)) (later Firefox), did not -want to be left behind. To allow people to do similarly neat things in -_its_ browser, Mozilla copied the interface, including the bogus name. -The next generation of ((browser))s followed this example, and today -`XMLHttpRequest` is a de facto standard ((interface)). - -## Sending a request - -{{index "open method", "send method", XMLHttpRequest}} - -To make a simple -((request)), we create a request object with the `XMLHttpRequest` -constructor and call its `open` and `send` methods. - -```{test: trim} -var req = new XMLHttpRequest(); -req.open("GET", "example/data.txt", false); -req.send(null); -console.log(req.responseText); -// → This is the content of data.txt -``` - -{{index [path, URL], "open method", "relative URL", "slash character"}} +`GET` requests should be used for requests that do not have ((side +effect))s, but simply ask for information. Requests that change +something on the server, for example creating a new account or posting +a message, should be expressed with other methods, such as `POST`. +Client-side software, for example a browser, knows that it shouldn't +blindly make `POST` requests but will often implicitly make `GET` +requests—for example to prefetch a resource it believes the user will +soon need. -The `open` -method configures the request. In this case, we choose to make a `GET` -request for the _example/data.txt_ file. ((URL))s that don't start -with a protocol name (such as _http:_) are relative, which means that -they are interpreted relative to the current document. When they start -with a slash (/), they replace the current path, which is the part after the -server name. When they do not, the part of the current path up to -and including its last slash character is put in front of the relative -URL. +We'll come back to forms and how to interact with them from JavaScript +[later in the chapter](http#forms). -{{index "send method", "GET method", "body (HTTP)", "responseText property"}} +{{id fetch}} -After opening the request, we can send it with the `send` -method. The argument to send is the request body. For `GET` requests, -we can pass `null`. If the third argument to `open` was `false`, `send` -will return only after the response to our request was received. We -can read the request object's `responseText` property to get the -response body. +## Fetch -{{index "status property", "statusText property", header, "getResponseHeader method"}} +{{index "fetch function", "Promise class"}} -The other -information included in the response can also be extracted from this -object. The ((status code)) is accessible through the `status` -property, and the human-readable status text is accessible through `statusText`. -Headers can be read with `getResponseHeader`. +The ((interface)) through which browser JavaScript can make HTTP +requests is called `fetch`. Since it is relatively new, it +conveniently uses promises (which is rare for browser interfaces). ```{test: no} -var req = new XMLHttpRequest(); -req.open("GET", "example/data.txt", false); -req.send(null); -console.log(req.status, req.statusText); -// → 200 OK -console.log(req.getResponseHeader("content-type")); -// → text/plain +fetch("example/data.txt").then(response => { + console.log(response.status); + // → 200 + console.log(response.headers.get("Content-Type")); + // → text/plain +}); ``` -{{index "case sensitivity", capitalization}} +{{index "Response class", "status property", "headers property"}} -Header names are -case-insensitive. They are usually written with a capital letter at -the start of each word, such as “Content-Type”, but “content-type” and -“cOnTeNt-TyPe” refer to the same header. +Calling `fetch` returns a promise that resolves to a `Response` object +holding information about the server's response, such as its status +code and its headers. The headers are wrapped in a `Map`-like object +that treats its keys (the header names) as case-insensitive, because +header names are not supposed to be case sensitive. This means that +`headers.get("Content-Type")` and `headers.get("content-TYPE")` will +return the same value. -{{index "Host header", "setRequestHeader method"}} +Note that the promise returned by `fetch` resolves successfully even +if the server responded with an error code. It _might_ also be +rejected, if there is a network error or the ((server)) that the +request is addressed to can not be found. -The browser will -automatically add some request ((header))s, such as “Host” and those -needed for the server to figure out the size of the body. But you can -add your own headers with the `setRequestHeader` method. This is -needed only for advanced uses and requires the cooperation of the -((server)) you are talking to—a server is free to ignore headers it -does not know how to handle. +{{index [path, URL], "relative URL"}} -## Asynchronous Requests +The first argument to `fetch` is the URL that should be requested. +What that ((URL)) doesn't start with a protocol name (such as _http:_) +it is treated as relative, which means that it is interpreted relative +to the current document. When they start with a slash (/), they +replace the current path, which is the part after the server name. +When they do not, the part of the current path up to and including its +last ((slash character)) is put in front of the relative URL. -{{index XMLHttpRequest, "event handling", blocking, "synchronous I/O", "responseText property", "send method"}} +{{index "text method", "body (HTTP)", "Promise class"}} -In the examples we -saw, the request has finished when the call to `send` returns. This is -convenient because it means properties such as `responseText` are -available immediately. But it also means that our program is suspended -as long as the ((browser)) and server are communicating. When the -((connection)) is bad, the server is slow, or the file is big, that -might take quite a while. Worse, because no event handlers can fire -while our program is suspended, the whole document will become -unresponsive. +To get at the actual content of a response, you can use its `text` +method. Because the initial promise is resolved as soon as the +response's headers have been received, and reading the response body +might take a while longer, this again returns a promise. -{{index XMLHttpRequest, "open method", "asynchronous I/O"}} +```{test: no} +fetch("example/data.txt") + .then(resp => resp.text()) + .then(text => console.log(text)); +// → This is the content of data.txt +``` -If we pass -`true` as the third argument to `open`, the request is _asynchronous_. -This means that when we call `send`, the only thing that happens right -away is that the request is scheduled to be sent. Our program can -continue, and the browser will take care of the sending and receiving -of data in the background. +{{index "json method"}} -But as long as the request is running, we won't be able to access the -response. We need a mechanism that will notify us when the data is -available. +There is a similar method, called `json`, which returns a promise that +resolves to the value you get when parsing the body as ((JSON)), or +rejects if it's not valid JSON. -{{index "event handling", "load event"}} +{{index "GET method", "body (HTTP)", "DELETE method", "method property"}} -For this, we must listen for the -`"load"` event on the request object. +By default, `fetch` uses the `GET` method to make its request, and +does not include a request body. You can configure it differently by +passing an object with extra options as a second argument. For +example, this request tries to delete `example/data.txt`. -``` -var req = new XMLHttpRequest(); -req.open("GET", "example/data.txt", true); -req.addEventListener("load", function() { - console.log("Done:", req.status); +```{test: no} +fetch("example/data.txt", {method: "DELETE"}).then(resp => { + console.log(resp.status); + // → 405 }); -req.send(null); -``` - -{{index "asynchronous programming", "callback function"}} - -Just like the use -of `requestAnimationFrame` in [Chapter ?](game), this -forces us to use an asynchronous style of programming, wrapping the -things that have to be done after the request in a function and -arranging for that to be called at the appropriate time. We will come -back to this [later](http#promises). - -## Fetching XML Data - -{{index "documentElement property", "responseXML property"}} - -When the -resource retrieved by an `XMLHttpRequest` object is an ((XML)) -document, the object's `responseXML` property will hold a parsed -representation of this document. This representation works much like -the ((DOM)) discussed in [Chapter ?](dom), except that -it doesn't have HTML-specific functionality like the `style` property. -The object that `responseXML` holds corresponds to the `document` -object. Its `documentElement` property refers to the outer tag of the -XML document. In the following document (_example/fruit.xml_), that -would be the `` tag: - -```{lang: "application/xml"} - - - - - ``` -We can retrieve such a file like this: +{{index "405 (HTTP status code)"}} -```{test: no} -var req = new XMLHttpRequest(); -req.open("GET", "example/fruit.xml", false); -req.send(null); -console.log(req.responseXML.querySelectorAll("fruit").length); -// → 3 -``` +The 405 status code means "method not allowed", an HTTP server's way +of saying "I can't do that". -{{index "data format"}} +{{index "Range header", "body property", "headers property"}} -XML documents can be used to exchange structured -information with the server. Their form—tags nested inside other -tags—lends itself well to storing most types of data, or at least -better than flat text files. The DOM interface is rather clumsy for -extracting information, though, and ((XML)) documents tend to be -verbose. It is often a better idea to communicate using ((JSON)) data, -which is easier to read and write, both for programs and for humans. +To add a request body, you can include a `body` option. And to set +headers, you'd use the `headers` option. For example, this request +includes a `Range` header, which instructs the server to only return a +part of a response. -``` -var req = new XMLHttpRequest(); -req.open("GET", "example/fruit.json", false); -req.send(null); -console.log(JSON.parse(req.responseText)); -// → {banana: "yellow", lemon: "yellow", cherry: "red"} +```{test: no} +fetch("example/data.txt", {headers: {Range: "bytes: 8-19"}}) + .then(resp => resp.text()) + .then(console.log); +// → This is the ``` +The browser will automatically add some request ((header))s, such as +"Host" and those needed for the server to figure out the size of the +body. But adding your own headers is often useful to include things +like authentication information or the tell the server which file +format you'd like to receive. + {{id http_sandbox}} + ## HTTP sandboxing {{index sandbox}} -Making ((HTTP)) requests in web page scripts once -again raises concerns about ((security)). The person who controls the -script might not have the same interests as the person on whose -computer it is running. More specifically, if I visit _themafia.org_, -I do not want its scripts to be able to make a request to -_mybank.com_, using identifying information from my ((browser)), with -instructions to transfer all my money to some random ((mafia)) -account. - -It is possible for ((website))s to protect themselves against such -((attack))s, but that requires effort, and many websites fail to do it. +Making ((HTTP)) requests in web page scripts once again raises +concerns about ((security)). The person who controls the script might +not have the same interests as the person on whose computer it is +running. More specifically, if I visit _themafia.org_, I do not want +its scripts to be able to make a request to _mybank.com_, using +identifying information from my ((browser)), with instructions to +transfer all my money to some random account. + For this reason, browsers protect us by disallowing scripts to make HTTP requests to other _((domain))s_ (names such as _themafia.org_ and _mybank.com_). {{index "Access-Control-Allow-Origin header", "cross-domain request"}} -This -can be an annoying problem when building systems that want to access -several domains for legitimate reasons. Fortunately, ((server))s can -include a ((header)) like this in their ((response)) to explicitly -indicate to browsers that it is okay for the request to come from -other domains: +This can be an annoying problem when building systems that want to +access several domains for legitimate reasons. Fortunately, +((server))s can include a ((header)) like this in their ((response)) +to explicitly indicate to browsers that it is okay for the request to +come from other domains: ```{lang: null} Access-Control-Allow-Origin: * ``` -## Abstracting requests - -{{index HTTP, XMLHttpRequest, "backgroundReadFile function"}} - -In -[Chapter ?](modules#amd), in our implementation of the AMD -module system, we used a hypothetical function called -`backgroundReadFile`. It took a filename and a function and called -that function with the contents of the file when it had finished -fetching it. Here's a simple implementation of that function: - -```{includeCode: true} -function backgroundReadFile(url, callback) { - var req = new XMLHttpRequest(); - req.open("GET", url, true); - req.addEventListener("load", function() { - if (req.status < 400) - callback(req.responseText); - }); - req.send(null); -} -``` - -{{index XMLHttpRequest}} - -This simple ((abstraction)) makes it easier to use -`XMLHttpRequest` for simple `GET` requests. If you are writing a -program that has to make HTTP requests, it is a good idea to use a -helper function so that you don't end up repeating the ugly -`XMLHttpRequest` pattern all through your code. - -{{index [function, "as value"], "callback function"}} - -The function argument's -name, `callback`, is a term that is often used to describe functions -like this. A callback function is given to other code to provide that -code with a way to “call us back” later. - -{{index library}} - -It is not hard to write an HTTP utility function, tailored to what your -application is doing. The previous one does only `GET` requests and -doesn't give us control over the headers or the request body. You -could write another variant for `POST` requests or a more generic one -that supports various kinds of requests. Many JavaScript libraries -also provide wrappers for `XMLHttpRequest`. - -{{index "user experience", "error response"}} - -The main problem with the previous -wrapper is its handling of ((failure)). When the request returns -a ((status code)) that indicates an error (400 and up), it does -nothing. This might be okay, in some circumstances, but imagine we put -a “loading” indicator on the page to indicate that we are fetching -information. If the request fails because the server crashed or the -((connection)) is briefly interrupted, the page will just sit there, -misleadingly looking like it is doing something. The user will wait -for a while, get impatient, and consider the site uselessly flaky. - -We should also have an option to be notified when the request fails -so that we can take appropriate action. For example, we could remove the -“loading” message and inform the user that something went wrong. - -{{index "exception handling", "callback function", "error handling", "asynchronous programming", "try keyword", stack}} - -Error handling in asynchronous code is even -trickier than error handling in synchronous code. Because we often need -to defer part of our work, putting it in a callback function, the -scope of a `try` block becomes meaningless. In the following code, the -exception will _not_ be caught because the call to -`backgroundReadFile` returns immediately. Control then leaves the -`try` block, and the function it was given won't be called until -later. - -```{test: no} -try { - backgroundReadFile("example/data.txt", function(text) { - if (text != "expected") - throw new Error("That was unexpected"); - }); -} catch (e) { - console.log("Hello from the catch block"); -} -``` - -{{index HTTP, "getURL function", exception}} - -{{id getURL}} -To handle failing -requests, we have to allow an additional function to be passed to our -wrapper and call that when a request goes wrong. Alternatively, we -can use the convention that if the request fails, an additional -argument describing the problem is passed to the regular callback -function. Here's an example: - -```{includeCode: true} -function getURL(url, callback) { - var req = new XMLHttpRequest(); - req.open("GET", url, true); - req.addEventListener("load", function() { - if (req.status < 400) - callback(req.responseText); - else - callback(null, new Error("Request failed: " + - req.statusText)); - }); - req.addEventListener("error", function() { - callback(null, new Error("Network error")); - }); - req.send(null); -} -``` - -{{index "error event"}} - -We have added a handler for the `"error"` event, -which will be signaled when the request fails entirely. We also call -the ((callback function)) with an error argument when the request -completes with a ((status code)) that indicates an error. - -Code using `getURL` must then check whether an error was given and, if -it finds one, handle it. - -``` -getURL("data/nonsense.txt", function(content, error) { - if (error != null) - console.log("Failed to fetch nonsense.txt: " + error); - else - console.log("nonsense.txt: " + content); -}); -``` - -{{index "uncaught exception", "exception handling", "try keyword"}} - -This -does not help when it comes to exceptions. When chaining several -asynchronous actions together, an exception at any point of the chain -will still (unless you wrap each handling function in its own -`try/catch` block) land at the top level and abort your chain of -actions. - -FIXME promise section removed here - ## Appreciating HTTP {{index client, HTTP}} -When building a system that requires -((communication)) between a JavaScript program running in the -((browser)) (client-side) and a program on a ((server)) (server-side), -there are several different ways to model this communication. +When building a system that requires ((communication)) between a +JavaScript program running in the ((browser)) (client-side) and a +program on a ((server)) (server-side), there are several different +ways to model this communication. {{index network, abstraction}} -A commonly used model is that of -_((remote procedure call))s_. In this model, communication follows the -patterns of normal function calls, except that the function is -actually running on another machine. Calling it involves making a -request to the server that includes the function's name and arguments. -The response to that request contains the returned value. +A commonly used model is that of _((remote procedure call))s_. In this +model, communication follows the patterns of normal function calls, +except that the function is actually running on another machine. +Calling it involves making a request to the server that includes the +function's name and arguments. The response to that request contains +the returned value. When thinking in terms of remote procedure calls, HTTP is just a vehicle for communication, and you will most likely write an @@ -649,90 +396,81 @@ abstraction layer that hides it entirely. {{index "media type", "document format"}} -Another approach is to build your -communication around the concept of ((resource))s and ((HTTP)) -((method))s. Instead of a remote procedure called `addUser`, you use a -`PUT` request to `/users/larry`. Instead of encoding that user's -properties in function arguments, you define a document format or use -an existing format that represents a user. The body of the `PUT` request -to create a new resource is then simply such a document. A resource is -fetched by making a `GET` -request to the resource's URL (for example, `/user/larry`), which -returns the document representing the resource. +Another approach is to build your communication around the concept of +((resource))s and ((HTTP)) ((method))s. Instead of a remote procedure +called `addUser`, you use a `PUT` request to `/users/larry`. Instead +of encoding that user's properties in function arguments, you define a +JSON document format (or use an existing format) that represents a +user. The body of the `PUT` request to create a new resource is then +such a document. A resource is fetched by making a `GET` request to +the resource's URL (for example, `/user/larry`), which again returns +the document representing the resource. This second approach makes it easier to use some of the features that HTTP provides, such as support for caching resources (keeping a copy -on the client side). It can also help the coherence of your interface -since resources are easier to reason about than a jumble of functions. +on the client for fast access). It can also provide a helpful set of +principles to design your interface around. ## Security and HTTPS {{index "man-in-the-middle", security, HTTPS}} -Data traveling over -the Internet tends to follow a long, dangerous road. To get -to its destination, it must hop through anything from coffee-shop Wi-Fi -((network))s to networks controlled by various companies and states. -At any point along its route it may be inspected or even modified. +Data traveling over the Internet tends to follow a long, dangerous +road. To get to its destination, it must hop through anything from +coffee-shop Wi-Fi to ((network))s controlled by various companies and +states. At any point along its route it may be inspected or even +modified. {{index tampering}} -If it is important that something remain secret, -such as the ((password)) to your ((email)) account, or that it arrive -at its destination unmodified, such as the account number you transfer -money to from your bank's website, plain HTTP is not good enough. +If it is important that something remain secret, such as the +((password)) to your ((email)) account, or that it arrive at its +destination unmodified, such as the account number you transfer money +to from your bank's website, plain HTTP is not good enough. {{index cryptography, encryption}} {{indexsee "Secure HTTP", HTTPS}} -The secure ((HTTP)) protocol, whose -((URL))s start with _https://_, wraps HTTP traffic in a way that makes -it harder to read and tamper with. First, the client verifies that the -server is who it claims to be by requiring that server to prove that it has a -cryptographic ((certificate)) issued by a certificate authority that -the ((browser)) recognizes. Next, all data going over the -((connection)) is encrypted in a way that should prevent eavesdropping -and tampering. - -Thus, when it works right, ((HTTPS)) prevents both the -someone impersonating the website you were trying to talk to and the -someone snooping on your communication. It is not -perfect, and there have been various incidents where HTTPS failed because of -forged or stolen certificates and broken software. Still, plain -HTTP is trivial to mess with, whereas breaking HTTPS requires the kind -of effort that only states or sophisticated criminal organizations can -hope to make. - - +The secure ((HTTP)) protocol, whose ((URL))s start with _https://_, +wraps HTTP traffic in a way that makes it harder to read and tamper +with. Before exchanging data, the client verifies that the server is +who it claims to be, by asking it to prove that it has a cryptographic +((certificate)) issued by a certificate authority that the ((browser)) +recognizes. Next, all data going over the ((connection)) is encrypted +in a way that should prevent eavesdropping and tampering. +Thus, when it works right, ((HTTPS)) prevents both the someone +impersonating the website you were trying to talk to and the someone +snooping on your communication. It is not perfect, and there have been +various incidents where HTTPS failed because of forged or stolen +certificates and broken software, but it is a _lot_ safer than plain +HTTP. +{{id forms}} -## Forms and Form Fields +## Form fields -Forms were introduced briefly in the -[previous chapter](http#http_forms) as a way to -_((submit))_ information provided by the user over ((HTTP)). They were -designed for a pre-JavaScript Web, assuming that interaction with the -server always happens by navigating to a new page. +Forms were originally designed for the pre-JavaScript Web, to allow +web sites to send user-submitted information in an HTTP request. This +design assumes that interaction with the server always happens by +navigating to a new page. But their elements are part of the ((DOM)) like the rest of the page, and the DOM elements that represent form ((field))s support a number of properties and events that are not present on other elements. These -make it possible to inspect and control such input fields with JavaScript programs -and do things such as adding functionality to a traditional form or using forms -and fields as building blocks in a JavaScript application. - -## Fields +make it possible to inspect and control such input fields with +JavaScript programs and do things such as adding new functionality to +a form or using forms and fields as building blocks in a JavaScript +application. {{index "form (HTML tag)"}} -A web form consists of any number of input -((field))s grouped in a `` tag. HTML allows a number of -different styles of fields, ranging from simple on/off checkboxes to -drop-down menus and fields for text input. This book won't try to -comprehensively discuss all field types, but we will start with a rough -overview. +A web form consists of any number of input ((field))s grouped in a +`` tag. HTML allows several different styles of fields, ranging +from simple on/off checkboxes to drop-down menus and fields for text +input. This book won't try to comprehensively discuss all field types, +but we will start with a rough overview. {{index "input (HTML tag)", "type attribute"}} @@ -752,11 +490,11 @@ field's style. These are some commonly used `` types: {{index "value attribute", "checked attribute", "form (HTML tag)"}} -Form -fields do not necessarily have to appear in a `` tag. You can -put them anywhere in a page. Such fields cannot be ((submit))ted -(only a form as a whole can), but when responding to input with -JavaScript, we often do not want to submit our fields normally anyway. +Form fields do not necessarily have to appear in a `` tag. You +can put them anywhere in a page. Such form-less fields cannot be +((submit))ted (only a form as a whole can), but when responding to +input with JavaScript, we often don't want to submit our fields +normally anyway. ```{lang: "text/html"}

(text)

@@ -777,16 +515,15 @@ The fields created with this HTML code look like this: if}} The JavaScript interface for such elements differs with the type of -the element. We'll go over each of them later in the chapter. +the element. {{index "textarea (HTML tag)", "text field"}} -Multiline text fields have -their own tag, `` closing tag and uses the text -between those two, instead of using its `value` attribute, as starting -text. +Multiline text fields have their own tag, `` closing +tag and uses the text between those two, instead of `value` attribute, +as starting text. ```{lang: "text/html"} ``` @@ -1075,15 +809,15 @@ a handler for the `"input"` event instead, which fires for every time the user types a character, deletes text, or otherwise manipulates the field's content. -The following example shows a text field and a counter showing the current -length of the text entered: +The following example shows a text field and a counter showing the +current length of the text in the field: ```{lang: "text/html"} length: 0 @@ -1093,16 +827,17 @@ length of the text entered: {{index "input (HTML tag)", "checked attribute"}} -A ((checkbox)) field is a -simple binary toggle. Its value can be extracted or changed through -its `checked` property, which holds a Boolean value. +A ((checkbox)) field is a simple binary toggle. Its value can be +extracted or changed through its `checked` property which holds a +Boolean value. ```{lang: "text/html"} - - + ``` -{{index "getElementsByName method", "name attribute", "array-like object", "event handling", "target property"}} +{{index "name attribute", "querySelectorAll method"}} -The -`document.getElementsByName` method gives us all elements with a given -`name` attribute. The example loops over those (with a regular `for` -loop, not `forEach`, because the returned collection is not a real -array) and registers an event handler for each element. Remember that -event objects have a `target` property referring to the element that -triggered the event. This is often useful in event handlers like this -one, which will be called on different elements and need some way to -access the current target. +The ((square brackets)) in the CSS query given to `querySelectorAll` +are used to query elements for a matching attribute. In this case, it +selects elements whose `name` attribute is `"color"`. ## Select fields @@ -1187,23 +915,14 @@ shows the options only when you open it. if}} -{{index "size attribute"}} - -The `size` attribute to the -`` element reflects -the currently selected option. For a `multiple` field, though, this -property doesn't mean much since it will give the value of only _one_ -of the currently selected options. +Each `