# JSON

  • JSON is JavaScript Object Notation
  • Lightweight data exchange format that is easily for humans to read or write
    轻量级数据交换格式,便于人类读写
  • Plain text format that is language independent but was based on a subset of JavaScript language syntax
    独立于语言但基于 JavaScript 语言语法子集的纯文本格式
  • Usable in most languages so it is an ideal data-interchange format
    可在大多数语言中使用,因此是一种理想的数据交换格式
  • JSON is built on two universal data structures that map to almost all programming languages
    JSON 构建在两种通用数据结构之上,这两种数据结构几乎映射到所有编程语言
Collection of name/value pairs name/value 对的集合
  • Realized in languages as object, record, struct, dictionary, hash table, keyed list, or associative array
    以对象、记录、结构、字典、哈希表、键表或关联数组等语言实现
    • In JSON an JavaScript this is an Object
    • Unordered set of name/value pairs
      name/value 对的无序集合
Ordered list of values 值的有序列表
  • Realized in languages as array, vector, list, or sequence
    以数组、向量、列表或序列等语言实现
    • In JSON and JavaScript this is an Array
    • Ordered collection of values
      值的有序集合

# Standard

  • Standardized by ECMA International as ECMA-404

  • Property names must be double quoted
    属性名称必须用双引号引起来

  • String values must be double quoted
    字符串值必须用双引号引起来

  • Trailing commas are forbidden
    禁止使用尾随逗号

  • Numbers can not have leading zeros
    数字不能有前导零

  • If a number has a decimal point, it must be followed by at least one digit
    如果数字有小数点,则后面必须至少跟一个数字

  • NaN and Infinity are not supported
    不支持 NaNInfinity

  • Values can be:

    • object
    • array
    • string
    • number
    • boolean value of true or false
    • null

JSON Example that could represent a person https://en.wikipedia.org/wiki/JSON

# Methods

  • JavaScript provides a JSON object which contains methods for parsing JSON text to JavaScript objects and converting JavaScript objects to JSON text.
    JavaScript 提供了一个 JSON 对象,其中包含将 JSON 文本解析为 JavaScript 对象并将 JavaScript 对象转换为 JSON 文本的方法。
  • These methods are available on the JSON object
    这些方法在 JSON 对象上可用
JSON.parse(text)
The parse method will convert the JSON formatted text string that is passed to it to the corresponding JavaScript objects
解析方法将传递给它的 JSON 格式的文本字符串转换为相应的 JavaScript 对象
JSON.stringify(value)
The stringify method will convert JavaScript objects to the corresponding JSON formatted text
Stringify 方法将 JavaScript 对象转换为相应的 JSON 格式文本

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON

# AJAX

  • AJAX – Asynchronous JavaScript And XML
    异步 JavaScript 和 XML
  • AJAX is not a programming or markup language
    AJAX 不是编程或标记语言
  • AJAX is a combination of other technologies:
    AJAX 是其他技术的组合:
    • Browser built-in object XMLHttpRequest to request data from a server
      浏览器内置对象 XMLHttpRequest ,用于从服务器请求数据
    • JavaScript and HTML DOM to modify the page once data is received
      接收到数据后修改页面的 JavaScript 和 HTML DOM
  • Use of XML is not required even though it is in the name. XML, JSON, text/HTML are common
    即使名称中有 XML,也不需要使用它。XML、JSON、文本 / HTML 很常见
  • Request is sent to server and response is received asynchronously and processed by JavaScript without a full page reload. This allows you to update parts of a web page without reloading the entire page.
    请求被发送到服务器,响应被异步接收并由 JavaScript 处理,而无需重新加载整个页面。这允许您更新网页的部分内容,而无需重新加载整个页面。

# Basic Steps

  • Event occurs in page that triggers AJAX request (click or something)
    触发 AJAX 请求的页面中发生事件 (单击或其他)
  • XMLHttpRequest object is created and configured
    创建并配置 XMLHttpRequest 对象
  • The XMLHttpRequest object sends an asynchronous request to the server
    XMLHttpRequest 对象向服务器发送异步请求
  • Server processes the request in back-end code
    服务器在后端代码中处理请求
  • Server sends a response back to the XMLHttpRequest object including the results as response text. Check readyState and status to see if success.
    服务器将响应发送回 XMLHttpRequest 对象,其中包括作为响应文本的结果。检查 ReadyState 和 Status 以查看是否成功。
  • The XMLHttpRequest object uses the configured callback function to run and process the results if successful, or proper error response if not
    如果成功,XMLHttpRequest 对象使用配置的回调函数运行和处理结果,如果不成功则使用适当的错误响应
  • JavaScript in the callback function updates the HTML, DOM, and CSS of the page as needed
    回调函数中的 javascript 根据需要更新页面的 html、 dom 和 css

# XMLHttpRequest XHR

  • All modern browsers and IE 7 + support the native XMLHttpRequest object.
    所有现代浏览器和 IE 7 + 都支持原生 XMLHttpRequest 对象。
  • For IE < 7 support you would need to use the active x version. (not a problem anymore, if interested there are resources online that talk about this)
  • Important Properties and Methods if xhr === XMLHttpRequest
    • xhr.onreadystatechange
    • xhr.readyState
    • xhr.status
    • xhr.responseText
    • xhr.open();
    • xhr.send();
  • Ready State is an unsigned int
    就绪状态是无符号整型
    • Can use constant as comparison instead of the number.
      可以使用常量作为比较,而不是数字。
      XMLHttpRequest.DONE same as 4
  • https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
  • https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest

# Same-Origin Policy

  • Script code must come from same server, domain, subdomain, protocol, port as the ajax call
    脚本代码必须来自与 ajax 调用相同的服务器、域、子域、协议和端口
    • http://en.wikipedia.org/wiki/Same_origin_policy
  • CORS will allow for cross-origin requests
    • https://enable-cors.org/
    • https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
    • Basic CORS support can be achieved as long as the cross-origin resource providing the data sets the proper header
      只要跨源资源提供了合适的数据集头,就可以实现基本的 cors 支持
      • Access-Control-Allow-Origin: *

# Basic Example

var myRequest = new XMLHttpRequest();
myRequest.onreadystatechange = function() {
    if (myRequest.readyState === 4) {
        if (myRequest.status === 200) {
            var myArray = JSON.parse(myRequest.responseText);
            parseData(myArray);
        }
    }
}
myRequest.open('GET', 'http://libertyville.rice.iit.edu/scripts/data.php', true);
myRequest.send();
function parseData(arr) {
    console.log(arr);
}
var myRequest = new XMLHttpRequest();
myRequest.onreadystatechange = function() {
    if (myRequest.readyState === XMLHttpRequest.DONE) {
        if (myRequest.status === 200) {
            var myArray = JSON.parse(myRequest.responseText);
            parseData(myArray);
        }
    }
};
myRequest.open('GET', 'http://libertyville.rice.iit.edu/scripts/data.php', true);
myRequest.send();
function parseData(arr) {
    console.log(arr);
}
var myRequest = new XMLHttpRequest();
myRequest.onreadystatechange = function(){
    if (myRequest.readyState === 4 && myRequest.status === 200) {
        var myArray = JSON.parse(myRequest.responseText);
        parseData(myArray);
    }
};
myRequest.open('GET', 'http://libertyville.rice.iit.edu/scripts/data.php', true);
myRequest.send();
function parseData(arr) {
    console.log(arr);
}

# AJAX Events and Monitoring

  • The XMLHttpRequest object allows us to listen for events that occur when the request is being processed, including progress, errors, and more.
    XMLHttpRequest 对象允许我们监听处理请求时发生的事件,包括进度、错误等等。
  • Must add before calling open()
  • Doesn't work on file:// protocol
  • Events:
    • progress
    • load
    • error
    • abort
    • loadend – happens at the end of all three (load, error, abort) events. Can not tell which event happened though. Useful for things that need to happen no matter which event happens.
      发生在所有三个 (加载、错误、中止) 事件的结尾。不知道发生了什么事。不管发生什么事情,对于需要发生的事情来说都是有用的。
  • An event object is the parameter of the event handler function
    事件对象是事件处理函数的参数
  • See section on monitoring progress https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest

# AJAX Example using event for load

var myRequest = new XMLHttpRequest();
myRequest.addEventListener('load', parseData);
myRequest.open('GET', 'http://libertyville.rice.iit.edu/scripts/data.php', true);
myRequest.send();
function parseData(evt) {
    console.log(evt);
    var myArray = JSON.parse(evt.target.responseText);
    // code to process the array and modify the DOM
}

# Fetch API

  • Fetch API is a promise based api for doing AJAX requests.
  • No support in IE but other modern browsers do support
  • https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
  • https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
  • https://developers.google.com/web/updates/2015/03/introduction-to-fetch
fetch('http://libertyville.rice.iit.edu/scripts/data.php')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson){
    console.log(JSON.stringify(myJson));
  });

# AJAX Libraries

  • There is Ajax support built-in to most JavaScript libraries including jQuery
    包括 jquery 在内的大多数 javascript 库都内置了 ajax 支持

  • jQuery ajax support is based on $.ajax(); function

  • See jQuery docs for api reference

  • Using libraries can simplify your use of Ajax
    使用库可以简化 ajax 的使用

  • New native fetch api is a more modern way to do ajax

  • Popular AJAX library is Axios

    • Promise based HTTP client for the browser and node.js
    • https://www.npmjs.com/package/axios

# AJAX POST & Data

# POST

  • Send data to a server typically with a POST request
    通常以发送请求的方式将数据发送到服务器
  • Datatype of the Body of the request is indicated by the Content-Type header
    请求主体的数据类型由 Content-Type 标头指示
  • HTML Forms typically submit using a POST request
    HTML 表单通常使用 post 请求提交
    • When sending by form the form tags enctype attribute will determine the content type
      当通过表单发送时,表单标记 enctype 属性将决定内容类型
    • application/x-www-form-urlencoded
      • Keys and values have an = between them
      • Key-value pairs are separated with an &
      • Non-alpha characters are percent encoded so not usable for binary data
        非字母字符采用百分比编码,因此不适用于二进制数据
    • multipart/form-data
      • Each value is sent as a block of data in the body with a delimiter between them
        每个值都以数据块的形式在主体中发送,数据块之间有分隔符
      • Use when binary data needs to be sent
        当需要发送二进制数据时使用
    • text/plain
  • If using AJAX to send a request the body can be of any data type you want
    • application/json is one example for JSON data
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST

# POST with XMLHttpRequest

  • You can use the XMLHttpRequest object's setRequestHeader() method to set headers on the request.
    • This is how you would set the Content-Type header to let the server know what the data type of the request body is.
  • The XMLHttpRequest object's send() method can take one parameter.
    • That parameter is the body of the request
    • https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/send

# POST with Fetch

  • Fetch defaults to a GET request so to send a POST request you need to use the optional init parameter to the fetch() method.
    Fetch 默认为 GET 请求,所以要发送 POST 请求,你需要给 fetch() 方法使用可选的 init 参数。
  • The init object is the second parameter to the fetch() method and it is an object
    init 对象是 fetch() 方法的第二个参数,它是一个对象
  • In that object you can set the body, headers, and request method, among other things
    在该对象中,您可以设置 body、header 和 request 方法等
  • Headers are set by creating a new Headers object and appending headers to it before setting it to the headers key in the init object.
    头信息的设置是通过创建一个新的 Headers 对象,并在将其设置为 init 对象中的 Headers 键之前附加头信息。
  • The body key can be a list of data types but is typically a string of JSON or formurlencoded data
    body 键可以是一个数据类型列表,但通常是一个 JSON 字符串或 forurlencoded 数据
  • https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch

# DEMO

AJAX Basic
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AJAX Basic</title>
</head>
<body>
    <button id="btn">Click Me!</button>
    <div id="results"></div>
    
    <script>
        document.getElementById('btn').addEventListener('click', function(){
            console.log('Button Clicked!');
            let myRequest = new XMLHttpRequest();
            myRequest.onreadystatechange = function(){
                if (myRequest.readyState === 4 && myRequest.status === 200) {
                    console.log(myRequest.responseText);
                    console.log(JSON.parse(myRequest.responseText));
                    let a = JSON.parse(myRequest.responseText);
                    let table = document.createElement('table');
                    for(let i = 0; i < a.length; i++) {
                        console.log(a[i]);
                        let row = document.createElement('tr');
                        row.id = a[i].id;
                        let cellproduct = document.createElement('td');
                        let protext = document.createTextNode(a[i].title);
                        cellproduct.appendChild(protext);
                        row.appendChild(cellproduct);
                        let cellpr = document.createElement('td');
                        let prtext = document.createTextNode(a[i].completed);
                        cellpr.appendChild(prtext);
                        row.appendChild(cellpr);
                        table.appendChild(row);
                    }
                    document.getElementById('results').appendChild(table);
                }
            };
            myRequest.open('GET', 'http://jsonplaceholder.typicode.com/todos');
            
            myRequest.send();
        }); //end button click event handler
    </script>
</body>
</html>
AJAX with Events
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AJAX with Events</title>
</head>
<body>
    <button id="btn">Click Me!</button>
    <div id="results"></div>
    <script>
        document.getElementById('btn').addEventListener('click', function(){
            console.log('Button Clicked!');
            let myRequest = new XMLHttpRequest();
            myRequest.addEventListener('load', function(evt){
                console.log(evt);
                let a = JSON.parse(evt.target.responseText);
                let table = document.createElement('table');
                for(let i = 0; i < a.length; i++) {
                    console.log(a[i]);
                    let row = document.createElement('tr');
                    row.id = a[i].id;
                    let cellproduct = document.createElement('td');
                    let protext = document.createTextNode(a[i].title);
                    cellproduct.appendChild(protext);
                    row.appendChild(cellproduct);
                    let cellpr = document.createElement('td');
                    let prtext = document.createTextNode(a[i].completed);
                    cellpr.appendChild(prtext);
                    row.appendChild(cellpr);
                    table.appendChild(row);
                }
                document.getElementById('results').appendChild(table);
            });
            myRequest.open('GET', 'http://jsonplaceholder.typicode.com/todos', true);	
            
            myRequest.send();
        }, false); //end button click event handler
    </script>
</body>
</html>
AJAX with Fetch
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AJAX with Fetch</title>
</head>
<body>
    <button id="btn">Click Me!</button>
    <div id="results"></div>
    
    <script>
        document.getElementById('btn').addEventListener('click', function(){
            console.log('Button Clicked!');
            fetch('http://jsonplaceholder.typicode.com/todos')
            .then(function(response){
                return response.json();
            })
            .then(function(myJson){
                console.log(myJson);
                let table = document.createElement('table');
                for(let i = 0; i < myJson.length; i++) {
                    console.log(myJson[i]);
                    let row = document.createElement('tr');
                    let cellproduct = document.createElement('td');
                    let protext = document.createTextNode(myJson[i].title);
                    cellproduct.appendChild(protext);
                    row.appendChild(cellproduct);
                    let cellpr = document.createElement('td');
                    let prtext = document.createTextNode(myJson[i].completed);
                    cellpr.appendChild(prtext);
                    row.appendChild(cellpr);
                    table.appendChild(row);
                }
                document.getElementById('results').appendChild(table);
            });
        }, false); //end button click event handler
    </script>
</body>
</html>
postback
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="formcontrols">
        <label>Name: <input type="text" name="name_in" id="name_in"></label><br>
        <label>Email: <input type="text" name="email_in" id="email_in"></label><br>
        <button id="btn-submit">POST with Fetch using JSON</button>
        <button id="btn-submit2">POST with Fetch using form url encoded</button>
        <button id="btn-submit3">POST with standard AJAX using JSON</button>
        <button id="btn-submit4">POST with standard AJAX using form url encoded</button>
    </div>
    <div id="requestdata">
        
    </div>
    <div id="results">
    </div>
    <script>
    document.addEventListener('DOMContentLoaded', function(){    
        document.getElementById('btn-submit').addEventListener('click', function(){
            let nameval = document.getElementById('name_in').value.trim();
            let emailval = document.getElementById('email_in').value.trim();
            let body = {name: nameval, email: emailval};
            let headers = new Headers();
            headers.append('Content-Type', 'application/json');
            fetch('postback.php', {
                method: 'post',
                body: JSON.stringify(body),
                headers: headers
            }).then(function(res){
                return res.json();
            }).then(function(data){
                console.log(data);
                printRequestDataToPage(JSON.stringify(body));
                printToPage(data);
            });
        });
        document.getElementById('btn-submit2').addEventListener('click', function(){
            let nameval = document.getElementById('name_in').value.trim();
            let emailval = document.getElementById('email_in').value.trim();
            let body = `name=${nameval}&email=${emailval}`;
            let headers = new Headers();
            headers.append('Content-Type', 'application/x-www-form-urlencoded');
            fetch('postback.php', {
                method: 'post',
                body: body,
                headers: headers
            }).then(function(res){
                return res.json();
            }).then(function(data){
                console.log(data);
                printRequestDataToPage(body);
                printToPage(data);
            });
        });
        document.getElementById('btn-submit3').addEventListener('click', function(){
            let nameval = document.getElementById('name_in').value.trim();
            let emailval = document.getElementById('email_in').value.trim();
            let body = {name: nameval, email: emailval};
            let xhr = new XMLHttpRequest();
            xhr.open('POST', 'postback.php', true);
            xhr.setRequestHeader('Content-Type', 'application/json');
            xhr.onreadystatechange = function(evt){
                if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
                    console.log(JSON.parse(this.responseText));
                    printRequestDataToPage(JSON.stringify(body));
                    printToPage(JSON.parse(this.responseText));
                }
            };
            xhr.send(JSON.stringify(body));
        });
        document.getElementById('btn-submit4').addEventListener('click', function(){
            let nameval = document.getElementById('name_in').value.trim();
            let emailval = document.getElementById('email_in').value.trim();
            let body = `name=${nameval}&email=${emailval}`;
            let xhr = new XMLHttpRequest();
            xhr.open('POST', 'postback.php', true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.onreadystatechange = function(evt){
                if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
                    console.log(JSON.parse(this.responseText));
                    printRequestDataToPage(body);
                    printToPage(JSON.parse(this.responseText));
                }
            };
            xhr.send(body);
        });
        function printToPage(JSONdata){
            let resultsDiv = document.getElementById('results');
            resultsDiv.innerHTML = '';
            let head1 = document.createElement('h3');
            head1.appendChild(document.createTextNode('JSON Response Data'));
            resultsDiv.appendChild(head1);
            let code = document.createElement('pre');
            code.style.marginTop = '20px';
            code.appendChild(document.createTextNode(JSON.stringify(JSONdata, undefined, 2)));
            resultsDiv.appendChild(code);
            let head2 = document.createElement('h3');
            head2.appendChild(document.createTextNode('Formatted Response Data'));
            resultsDiv.appendChild(head2);
            let values = document.createElement('div');
            values.style.marginTop = '20px';
            let codeTxt = `<b>name:</b> ${JSONdata.post_data.name}<br><b>email:</b> ${JSONdata.post_data.email}<br><b>method:</b> ${JSONdata.http_method}<br><b>Request Content Type:</b> ${JSONdata.request_content_type}<br><b>Timestamp:</b> ${JSONdata.time}<br>`;
            values.innerHTML = codeTxt;
            resultsDiv.appendChild(values);
        }
        function printRequestDataToPage(data) {
            let requestDiv = document.getElementById('requestdata');
            requestDiv.innerHTML = '';
            let head1 = document.createElement('h3');
            head1.appendChild(document.createTextNode('Request Data'));
            requestDiv.appendChild(head1);
            let code = document.createElement('pre');
            code.style.marginTop = '20px';
            code.appendChild(document.createTextNode(data));
            requestDiv.appendChild(code);
        }
    });//end DOMContentLoaded
    </script>
</body>
</html>
postback
<?php
if ( isset($_SERVER['CONTENT_TYPE']) && strtolower($_SERVER['CONTENT_TYPE']) == 'application/json' ) {
  $inputJSON = file_get_contents('php://input');
  $data = json_decode($inputJSON);
  $output = [ "post_data" => $data ];
  $output["time"] = date("Y-m-d H:i:s");
  $output["request_content_type"] = $_SERVER['CONTENT_TYPE'];
  $output["http_method"] = $_SERVER['REQUEST_METHOD'];
  header("Access-Control-Allow-Origin: *");
  header("Content-Type: application/json");
  echo json_encode($output);
} else {
  $output = ["post_data" => $_POST];
  $output["time"] = date("Y-m-d H:i:s");
  $output["request_content_type"] = $_SERVER['CONTENT_TYPE'];
  $output["http_method"] = $_SERVER['REQUEST_METHOD'];
  header("Access-Control-Allow-Origin: *");
  header("Content-Type: application/json");
  echo json_encode($output);
}