【译】经由过程JavaScript发送表单

在[发送表单数据]()一文中,HTML表单能够声明式地发送一个HTTP要求。但表单也能够用JavaScript来预备一个HTTP要求。本文将探究怎样做到这点。

表单,不只是表单

跟着开放式Web运用的涌现,如今供应HTML表单、而不是笔墨表单供用户填写的做法,已越发广泛了。而越来越多的开发者也取得了数据传输的掌握权。

取得对数据传输的掌握权

范例的HTML表单提交操纵会加载数据发送到的URL,这就意味着浏览器的窗口中会举行全部页面的从新加载。而假如防止了页面的从新加载,就会防止页面的闪灼和网络耽误,进而供应更顺畅的用户体验。

在许多当代的UI设想中,HTML表单只是用来网络用户的输入。当用户要发送数据时,Web运用会举行掌握,并在背景异步地发送数据,只更新UI中须要变动的部份。

能异步地发送恣意数据的手艺称为AJAX,示意“异步的JavaScript和XML”。

和传统表单处置惩罚的差别

AJAX运用了XMLHttpRequest(XHR)DOM对象,它能够竖立HTTP要求、发送要求并处置惩罚结果。

注重: 老的AJAX手艺能够不是用XMLHttpRequest。比方JSONPeval()函数结合起来运用。虽然该要领可行,但不引荐运用它,因为其存在严峻的平安题目。所以除非为了兼容那些迥殊老旧、不支撑XMLHttpRequest或JSON的浏览器,照样防止运用该手艺

因为汗青缘由,XMLHttpRequest本是设想用来猎取和发送交流花样为XML的数据的。但如今JSON庖代了XML,有着更广泛的运用。

不过XML和JSON都不相符作为表单数据要求的编码。表单数据(application/x-www-form-urlencoded)是用于构造键值对的URL编码列表的,如果要传输二进制数据,HTTP要求会被重塑为 multipart/form-data

若你能掌控前端(运行在浏览器上的代码)和后端(运行在效劳器上的代码),你就可以发送JSON或XML、并为所欲为地处置惩罚它们。

但假如你运用的是第三方效劳,这就没那末轻易了,因为某些效劳只接收表单数据。固然也有运用表单数据处置惩罚起来更轻易的状况,比方数据是键值对或二进制数据时,用现成的后端东西就可以处置惩罚它们、不须要分外的代码。

那末,细致该怎样发送数据呢?

发送表单数据

现在有三种体式格局来发送表单数据,既有老旧的手艺、也有新特征FormData对象,接下来就来深切相识下它们。

在隐蔽的iframe中构建DOM

发送表单数据最陈旧的要领,是用DOM API竖立一个表单,然后发送数据到一个隐蔽的<iframe>。为了接见你提交内容的处置惩罚结果,应检索下<iframe>的内容。

正告:防止运用该手艺。在运用第三方效劳时,该手艺有平安风险,因为它会致使你面对剧本注入进击。若你运用HTTPS,运用<ifrme>发送表单还会影响同源战略,并致使内容被发送到一个无法接见的<ifrme>中。固然假如你要兼容很老旧的浏览器,这项手艺能够就是你唯一的挑选了。

下面是一个例子:

<button onclick="sendData({test:'ok'})">Click Me!</button>
// 建立一个iFrame来发送我们的数据
var iframe = document.createElement("iframe");
iframe.name = "myTarget";

// 将iFrame增加到文档流中
window.addEventListener("load", function () {
  iframe.style.display = "none";
  document.body.appendChild(iframe);
});

// 用来发送数据的函数
// 须要一个参数,是一个由键值对构成的对象
function sendData(data) {
  var name,
      form = document.createElement("form"),
      node = document.createElement("input");

  // 定义相应加载时的行动
  iframe.addEventListener("load", function () {
    alert("Yeah! Data sent.");
  });
    
  form.action = "http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi";
  form.target = iframe.name;

  for(name in data) {
    node.name  = name;
    node.value = data[name].toString();
    form.appendChild(node.cloneNode());
  }

  // 要发送数据,表单得增加到文档流中
  form.style.display = "none";
  document.body.appendChild(form);

  form.submit();

  // 表单一发送就移除它
  document.body.removeChild(form);
}

结果以下:
结果

手动建立一个XMLHttpRequest

XMLHttpRequest是发送HTTP要求最平安和牢靠的体式格局。要想用XMLHttpRequest发送表单数据,得先用URL编码要发送的数据,并遵照表单数据要求的范例。

注重: 若想相识更多关于XMLHttpRequest,这几篇文章能够对你有效:An introductory article to AJAX,以及一个关于运用XMLHttpRequest的高等教程。

来重构下我们先前的例子:

<button type="button" onclick="sendData({test:'ok'})">Click Me!</button>

如你所见,HTML部份并未真的有所转变,但JavaScript部份就完整差别了:

function sendData(data) {
  var XHR = new XMLHttpRequest();
  var urlEncodedData = "";
  var urlEncodedDataPairs = [];
  var name;

  // 将data对象转为一个URL编码的键值对数组
  for(name in data) {
    urlEncodedDataPairs.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));
  }

  // 将键值对组合成一个字符串,并把一切经由URL编码的空格替换为'+'号;以相符浏览器表单提交的行动
  // the '+' character; matches the behaviour of browser form submissions.
  urlEncodedData = urlEncodedDataPairs.join('&').replace(/%20/g, '+');

  // 定义胜利的数据提交后会发作什么
  XHR.addEventListener('load', function(event) {
    alert('Yeah! Data sent and response loaded.');
  });

  // 定义失利的状况会发作什么
  XHR.addEventListener('error', function(event) {
    alert('Oups! Something goes wrong.');
  });

  // 设置要求
  XHR.open('POST', 'https://example.com/cors.php');

  // 增加表单数据POST要求所需的HTTP要求头
  XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

  // 末了,发送数据
  XHR.send(urlEncodedData);
}

结果以下:
结果

注重: 若你想运用此处的XMLHttpRequest要领发送数据到第三方网站,也会遭到同源战略的限定。要完成跨域要求,还须要CORS和HTTP接见掌握

运用XMLHttpRequest和FormData对象

手动构建HTTP要求是挺贫苦的,荣幸的是,近来的一项XMLHttpRequest范例供应了一个处置惩罚表单要求的轻便要领–FormData对象。

FormData对象可被用来竖立要传输的表单数据,或许从表单元素中猎取数据来治理怎样发送。要注重FormData对象是“只写”的,这意味着你能够转变它们、但不能接见它们的内容。

运用FormData对象的要领会在[运用FormData对象]()一文中细致引见,下面有两个例子:

运用自力的FormData对象

<button type="button" onclick="sendData({test:'ok'})">Click Me!</button>

如今你该很熟悉这个HTML的模样了。

function sendData(data) {
  var XHR = new XMLHttpRequest();
  var FD  = new FormData();

  // 将数据增加到FormData对象中
  for(name in data) {
    FD.append(name, data[name]);
  }

  // 定义胜利的数据提交后会发作什么
  XHR.addEventListener('load', function(event) {
    alert('Yeah! Data sent and response loaded.');
  });

  // 定义失利的状况会发作什么
  XHR.addEventListener('error', function(event) {
    alert('Oups! Something went wrong.');
  });

  // 设置要求
  XHR.open('POST', 'https://example.com/cors.php');

  // 发送FormData对象,HTTP头会自动设置
  XHR.send(FD);
}

结果以下:
结果

将FormData绑到表单元素上运用

你也能够将FormData对象绑定到一个表单元素上,如许做会建立一个FormData对象来示意表单中的数据。

HTML是典范的表单:

<form id="myForm">
  <label for="myName">Send me your name:</label>
  <input id="myName" name="name" value="John">
  <input type="submit" value="Send Me!">
</form>

但JavaScript会接受表单的提交操纵:

window.addEventListener("load", function () {
  function sendData() {
    var XHR = new XMLHttpRequest();

    // 绑定FormData对象和表单元素
    var FD = new FormData(form);

    // 定义胜利的数据提交后会发作什么
    XHR.addEventListener("load", function(event) {
      alert(event.target.responseText);
    });

    // 定义失利的状况会发作什么
    XHR.addEventListener("error", function(event) {
      alert('Oups! Something goes wrong.');
    });

    // 设置要求
    XHR.open("POST", "https://example.com/cors.php");

    // 发送的数据是用户在表单中供应的
    XHR.send(FD);
  }
 
  // 接见表单元素
  var form = document.getElementById("myForm");

  // 并接受其submit事宜
  form.addEventListener("submit", function (event) {
    event.preventDefault();

    sendData();
  });
});

结果以下:
结果

处置惩罚二进制数据

若你在一个含有<input type="file">组件的表单中运用FormData对象,那末数据会被自动处置惩罚。但要手动发送二进制数据的话,另有许多分外事情要做。

当代Web有许多二进制数据源:比方FileReaderCanvasWebRTC。但不幸的是,某些老旧浏览器不能接见二进制数据或许须要庞杂的要领才完成。这些遗留的题目已不在本文议论局限以内。若你想相识更多关于FileReader API,请浏览Using files from web applications

用FormData来发送二进制是很直接的,运用append()要领就好了。但要手动做到这点,就须要一些技巧了。

鄙人面的例子中,我们会用来FileReader API来接见二进制数据,然后手动竖立多部份的表单数据要求。

<form id="myForm">
  <p>
    <label for="i1">text data:</label>
    <input id="i1" name="myText" value="Some text data">
  </p>
  <p>
    <label for="i2">file data:</label>
    <input id="i2" name="myFile" type="file">
  </p>
  <button>Send Me!</button>
</form>

如你所见,HTML用了范例的<form>,这没什么奇异的。“奇异”的部份在JavaScript里:

// 因为我们要接见DOM结点,所以得在页面加载完后才初始化剧本
window.addEventListener('load', function () {

  // 这些变量用来存储表单数据
  var text = document.getElementById("i1");
  var file = {
        dom    : document.getElementById("i2"),
        binary : null
      };
 
  // 运用FileReader API来接见文件内容
  var reader = new FileReader();

  // 因为FileReader是异步的,所以得在其完成文件读取后才存储结果
  reader.addEventListener("load", function () {
    file.binary = reader.result;
  });

  // 在页面加载时,若已挑选了文件就直接读取它
  if(file.dom.files[0]) {
    reader.readAsBinaryString(file.dom.files[0]);
  }

  // 不然在用户挑选文件时再读取它
  file.dom.addEventListener("change", function () {
    if(reader.readyState === FileReader.LOADING) {
      reader.abort();
    }
    
    reader.readAsBinaryString(file.dom.files[0]);
  });

  // sendData是本例的重要函数
  function sendData() {
    // 若已挑选了文件,就等浏览器读取完
    // 不然就耽误本函数的实行
    if(!file.binary && file.dom.files.length > 0) {
      setTimeout(sendData, 10);
      return;
    }

    // 要构建多部份的表单数据要求,须要一个XMLHttpRequest实例
    var XHR = new XMLHttpRequest();

    // 须要一个分隔符来定义要求体的每部份
    var boundary = "blob";

    // 将要求体存为一个字符串
    var data = "";

    // 若用户挑选了文件
    if (file.dom.files[0]) {
      // 开启要求体的新部份
      data += "--" + boundary + "\r\n";

      // 该部份形貌为表单数据
      data += 'content-disposition: form-data; '
      // 定义表单数据的名字
            + 'name="'         + file.dom.name          + '"; '
      // 供应实在的文件名
            + 'filename="'     + file.dom.files[0].name + '"\r\n';
      // 供应文件的MIME范例
      data += 'Content-Type: ' + file.dom.files[0].type + '\r\n';

      // 元数据和实在数据部份间有一个空行
      data += '\r\n';
      
      // 往要求体里增加二进制数据
      data += file.binary + '\r\n';
    }

    // 文本数据的构造越发简朴
    // 开启要求体的新部份
    data += "--" + boundary + "\r\n";

    // 形貌为表单数据并定名
    data += 'content-disposition: form-data; name="' + text.name + '"\r\n';
    // 元数据和实在数据部份间有一个空行
    data += '\r\n';

    // 往要求体里增加文本数据
    data += text.value + "\r\n";

    // 完成了一切部份,就“闭合”要求体
    data += "--" + boundary + "--";

    // 定义胜利的数据提交后会发作什么
    XHR.addEventListener('load', function(event) {
      alert('Yeah! Data sent and response loaded.');
    });

    // 定义失利的状况会发作什么
    XHR.addEventListener('error', function(event) {
      alert('Oups! Something went wrong.');
    });

    // 设置要求
    XHR.open('POST', 'https://example.com/cors.php');

    // 增加必要的HTTP要求头来处置惩罚多部份表单数据的POST要求
    XHR.setRequestHeader('Content-Type','multipart/form-data; boundary=' + boundary);

    // 末了,发送数据
    XHR.send(data);
  }

  // 接见表单
  var form = document.getElementById("myForm");

  // 接受submit事宜
  form.addEventListener('submit', function (event) {
    event.preventDefault();
    sendData();
  });
});

结果以下:
结果

结论

浏览器的差别,致使经由过程JavaScript发送表单数据能够简朴或很难题。FromData对象是一般的解决方案,而且我们应当绝不犹豫地在老旧浏览器上运用其polyfill:

    原文作者:Levon
    原文地址: https://segmentfault.com/a/1190000010110721
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞