Exemplo de navegação Ajax
=========================

Esse é um exemplo de um web site em AJAX web site composto por apenas três páginas (*first\_page.php*, *second\_page.php* e *third\_page.php*). Para ver como funciona, crie os arquivos a seguir (ou *git clone* https://github.com/giabao/mdn-ajax-nav-example.git ):

**Nota:** Para integrar completamente os elementos `<form>` (en-US) com esse *mecanismo*, porfavor dê uma olhada no parágrafo Enviando formulários e enviando arquivos.

**first\_page.php**:


```php
<?php
 $page\_title = "Primeira página";

 $as\_json = false;
 if (isset($\_GET["view\_as"]) && $\_GET["view\_as"] == "json") {
 $as\_json = true;
 ob\_start();
 } else {
?>
<!doctype html>
<html>
<head>
<?php
 include "include/header.php";
 echo "<title>" . $page\_title . "</title>";
?>
</head>

<body>

<?php include "include/before\_content.php"; ?>

<p>Esse parágrafo só é mostrado quando a navegação começa em <strong>first_page.php</strong>.</p>

<div id="ajax-content">
<?php } ?>

    <p>Esse é o conteúdo de <strong>first_page.php</strong>.</p>

<?php
 if ($as\_json) {
 echo json\_encode(array("page" => $page\_title, "content" => ob\_get\_clean()));
 } else {
?>
</div>

<p>Esse parágrafo só é mostrado quando a navegação começa em <strong>first_page.php</strong>.</p>

<?php
 include "include/after\_content.php";
 echo "</body>\n</html>";
 }
?>
```


**second\_page.php**:


```php
<?php
 $page\_title = "Segunda página";

 $as\_json = false;
 if (isset($\_GET["view\_as"]) && $\_GET["view\_as"] == "json") {
 $as\_json = true;
 ob\_start();
 } else {
?>
<!doctype html>
<html>
<head>
<?php
 include "include/header.php";
 echo "<title>" . $page\_title . "</title>";
?>
</head>

<body>

<?php include "include/before\_content.php"; ?>

<p>Esse parágrafo só é mostrado quando a navegação começa em <strong>second_page.php</strong>.</p>

<div id="ajax-content">
<?php } ?>

    <p>Esse é o conteúdo de <strong>second_page.php</strong>.</p>

<?php
 if ($as\_json) {
 echo json\_encode(array("page" => $page\_title, "content" => ob\_get\_clean()));
 } else {
?>
</div>

<p>Esse parágrafo só é mostrado quando a navegação começa em <strong>second_page.php</strong>.</p>

<?php
 include "include/after\_content.php";
 echo "</body>\n</html>";
 }
?>
```


**third\_page.php**:


```php
<?php
 $page\_title = "Terceira página";
 $page\_content = "<p>Esse é o conteúdo de <strong>third\_page.php</strong>. This content is stored into a php variable.</p>";

 if (isset($\_GET["view\_as"]) && $\_GET["view\_as"] == "json") {
 echo json\_encode(array("page" => $page\_title, "content" => $page\_content));
 } else {
?>
<!doctype html>
<html>
<head>
<?php
 include "include/header.php";
 echo "<title>" . $page\_title . "</title>";
?>
</head>

<body>

<?php include "include/before\_content.php"; ?>

<p>Esse parágrafo só é mostrado quando a navegação começa em <strong>third_page.php</strong>.</p>

<div id="ajax-content">
<?php echo $page\_content; ?>
</div>

<p>Esse parágrafo só é mostrado quando a navegação começa em <strong>third_page.php</strong>.</p>

<?php
 include "include/after\_content.php";
 echo "</body>\n</html>";
 }
?>
```


**css/style.css**:


```css
#ajax-loader {
  position: fixed;
  display: table;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

#ajax-loader > div {
  display: table-cell;
  width: 100%;
  height: 100%;
  vertical-align: middle;
  text-align: center;
  background-color: #000000;
  opacity: 0.65;
}
```


**include/after\_content.php**:


```php
<p>Esse é o rodapé. Ele é compartilhado entre todas as páginas ajax.</p>
```


**include/before\_content.php**:


```php
<p>
[ <a class="ajax-nav" href="first\_page.php">Primeiro exemplo</a>
| <a class="ajax-nav" href="second\_page.php">Segundo exemplo</a>
| <a class="ajax-nav" href="third\_page.php">Terceiro exemplo</a>
| <a class="ajax-nav" href="unexisting.php">Página inexistente</a> ]
</p>
```


**include/header.php**:


```php
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="text/javascript" src="js/ajax\_nav.js"></script>
<link rel="stylesheet" href="css/style.css" />
```


**js/ajax\_nav.js**:

(antes de implementar em um ambiente de trabalho, **porfavor leia a nota sobre a compatibilidade de declaração de const**)


```js
"use strict";

const ajaxRequest = new (function () {
  function closeReq() {
    oLoadingBox.parentNode && document.body.removeChild(oLoadingBox);
    bIsLoading = false;
  }

  function abortReq() {
    if (!bIsLoading) {
      return;
    }
    oReq.abort();
    closeReq();
  }

  function ajaxError() {
    alert("Unknown error.");
  }

  function ajaxLoad() {
    var vMsg,
      nStatus = this.status;
    switch (nStatus) {
      case 200:
        vMsg = JSON.parse(this.responseText);
        document.title = oPageInfo.title = vMsg.page;
        document.getElementById(sTargetId).innerHTML = vMsg.content;
        if (bUpdateURL) {
          history.pushState(oPageInfo, oPageInfo.title, oPageInfo.url);
          bUpdateURL = false;
        }
        break;
      default:
        vMsg = nStatus + ": " + (oHTTPStatus[nStatus] || "Unknown");
        switch (Math.floor(nStatus / 100)) {
          /\*
 case 1:
 // Informational 1xx
 console.log("Information code " + vMsg);
 break;
 case 2:
 // Successful 2xx
 console.log("Successful code " + vMsg);
 break;
 case 3:
 // Redirection 3xx
 console.log("Redirection code " + vMsg);
 break;
 \*/
          case 4:
            /\* Client Error 4xx \*/
            alert("Client Error #" + vMsg);
            break;
          case 5:
            /\* Server Error 5xx \*/
            alert("Server Error #" + vMsg);
            break;
          default:
            /\* Unknown status \*/
            ajaxError();
        }
    }
    closeReq();
  }

  function filterURL(sURL, sViewMode) {
    return (
      sURL.replace(rSearch, "") +
      (
        "?" +
        sURL
          .replace(rHost, "&")
          .replace(rView, sViewMode ? "&" + sViewKey + "=" + sViewMode : "")
          .slice(1)
      ).replace(rEndQstMark, "")
    );
  }

  function getPage(sPage) {
    if (bIsLoading) {
      return;
    }
    oReq = new XMLHttpRequest();
    bIsLoading = true;
    oReq.onload = ajaxLoad;
    oReq.onerror = ajaxError;
    if (sPage) {
      oPageInfo.url = filterURL(sPage, null);
    }
    oReq.open("get", filterURL(oPageInfo.url, "json"), true);
    oReq.send();
    oLoadingBox.parentNode || document.body.appendChild(oLoadingBox);
  }

  function requestPage(sURL) {
    if (history.pushState) {
      bUpdateURL = true;
      getPage(sURL);
    } else {
      /\* Ajax navigation is not supported \*/
      location.assign(sURL);
    }
  }

  function processLink() {
    if (this.className === sAjaxClass) {
      requestPage(this.href);
      return false;
    }
    return true;
  }

  function init() {
    oPageInfo.title = document.title;
    history.replaceState(oPageInfo, oPageInfo.title, oPageInfo.url);
    for (
      var oLink, nIdx = 0, nLen = document.links.length;
      nIdx < nLen;
      document.links[nIdx++].onclick = processLink
    );
  }

  const /\* customizable constants \*/
    sTargetId = "ajax-content",
    sViewKey = "view\_as",
    sAjaxClass = "ajax-nav",
    /\* not customizable constants \*/
    rSearch = /\?.\*$/,
    rHost = /^[^\?]\*\?\*&\*/,
    rView = new RegExp("&" + sViewKey + "\\=[^&]\*|&\*$", "i"),
    rEndQstMark = /\?$/,
    oLoadingBox = document.createElement("div"),
    oCover = document.createElement("div"),
    oLoadingImg = new Image(),
    oPageInfo = {
      title: null,
      url: location.href,
    },
    oHTTPStatus =
      /\* http://www.iana.org/assignments/http-status-codes/http-status-codes.xml \*/ {
        100: "Continue",
        101: "Switching Protocols",
        102: "Processing",
        200: "OK",
        201: "Created",
        202: "Accepted",
        203: "Non-Authoritative Information",
        204: "No Content",
        205: "Reset Content",
        206: "Partial Content",
        207: "Multi-Status",
        208: "Already Reported",
        226: "IM Used",
        300: "Multiple Choices",
        301: "Moved Permanently",
        302: "Found",
        303: "See Other",
        304: "Not Modified",
        305: "Use Proxy",
        306: "Reserved",
        307: "Temporary Redirect",
        308: "Permanent Redirect",
        400: "Bad Request",
        401: "Unauthorized",
        402: "Payment Required",
        403: "Forbidden",
        404: "Not Found",
        405: "Method Not Allowed",
        406: "Not Acceptable",
        407: "Proxy Authentication Required",
        408: "Request Timeout",
        409: "Conflict",
        410: "Gone",
        411: "Length Required",
        412: "Precondition Failed",
        413: "Request Entity Too Large",
        414: "Request-URI Too Long",
        415: "Unsupported Media Type",
        416: "Requested Range Not Satisfiable",
        417: "Expectation Failed",
        422: "Unprocessable Entity",
        423: "Locked",
        424: "Failed Dependency",
        425: "Unassigned",
        426: "Upgrade Required",
        427: "Unassigned",
        428: "Precondition Required",
        429: "Too Many Requests",
        430: "Unassigned",
        431: "Request Header Fields Too Large",
        500: "Internal Server Error",
        501: "Not Implemented",
        502: "Bad Gateway",
        503: "Service Unavailable",
        504: "Gateway Timeout",
        505: "HTTP Version Not Supported",
        506: "Variant Also Negotiates (Experimental)",
        507: "Insufficient Storage",
        508: "Loop Detected",
        509: "Unassigned",
        510: "Not Extended",
        511: "Network Authentication Required",
      };

  var oReq,
    bIsLoading = false,
    bUpdateURL = false;

  oLoadingBox.id = "ajax-loader";
  oCover.onclick = abortReq;
  oLoadingImg.src =
    "data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==";
  oCover.appendChild(oLoadingImg);
  oLoadingBox.appendChild(oCover);

  onpopstate = function (oEvent) {
    bUpdateURL = false;
    oPageInfo.title = oEvent.state.title;
    oPageInfo.url = oEvent.state.url;
    getPage();
  };

  window.addEventListener
    ? addEventListener("load", init, false)
    : window.attachEvent
      ? attachEvent("onload", init)
      : (onload = init);

  // Public methods

  this.open = requestPage;
  this.stop = abortReq;
  this.rebuildLinks = init;
})();
```


**Nota:** A atual implementação de `const` (declaração de constante) **não é parte do ECMAScript 5**. É suportada no Firefox e no Chrome (V8) e parcialmente suportada no Opera 9+ e no Safari. **Ela não é suportada nas versões do Internet Explorer 6 ao 9, ou na versão *preview* do Internet Explorer 10**. `const` será definida no ECMAScript 6, mas com semânticas diferentes. Similarmente ao que acontece com variáveis definidas como `let`, constantes declaradas com `const` serão *block-scoped*, limitando seu escopo no bloco. **Nós só usamos isso com propósito didático. Se você quer total compatibilidade com os navegadores, substitua todas as declarações `const` por declarações `var`.**

Para mais informações, veja: Manipulando o histórico do navegador (en-US).

Veja também
-----------

* `window.history`
* `window.onpopstate` (en-US)