diff options
Diffstat (limited to 'files/ru/learn/forms/sending_forms_through_javascript')
| -rw-r--r-- | files/ru/learn/forms/sending_forms_through_javascript/index.html | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/files/ru/learn/forms/sending_forms_through_javascript/index.html b/files/ru/learn/forms/sending_forms_through_javascript/index.html new file mode 100644 index 0000000000..d98ccea1ac --- /dev/null +++ b/files/ru/learn/forms/sending_forms_through_javascript/index.html @@ -0,0 +1,391 @@ +--- +title: Отправка форм при помощи JavaScript +slug: Learn/HTML/Forms/Sending_forms_through_JavaScript +translation_of: Learn/Forms/Sending_forms_through_JavaScript +--- +<div>{{LearnSidebar}}</div> + +<p><span class="seoSummary">HTML формы могут декларативно отправлять <a href="/en-US/docs/HTTP">HTTP</a>-запросы. Но формы также могут подготовить HTTP-запросы для отправки с помощью JavaScript, например при помощи <code>XMLHttpRequest</code>. В этой статье исследуются подобные подходы.</span></p> + +<h2 id="Формы_не_всегда_формы">Формы не всегда формы</h2> + +<p>В современных веб-приложениях, одностраничных приложениях и приложениях на основе фреймворков, обычно <a href="/en-US/docs/HTML/Forms">HTML-формы</a> используются для отправки данных без загрузки нового документа при получении данных ответа. В начале поговорим о том почему это требует другого подхода.</p> + +<h3 id="Получение_контроля_над_глобальным_интерфейсом">Получение контроля над глобальным интерфейсом</h3> + +<p>Отправка стандартной HTML формы, как описывалось в предыдущей статье, загружает URL-адрес, по которому были отправлены данные, это означает, что окно браузера перемещается с полной загрузкой страницы. Если избегать полную перезагрузку страницы, можно обеспечить более плавную работу, за счет предотвращения задержек в сети и возможных визуальных проблем (например, мерцания).</p> + +<p>Многие современные пользовательские интерфейсы используют HTML формы только для сбора пользовательского ввода, а не для для отправки данных. Когда пользователь пытатся отправить свои данные, приложение берет контроль и асинхронно передает данные в фоновом режиме, обновляя только ту часть всего интерфейса пользователя, которой требуется обновление.</p> + +<p>Асинхронная отправка произвольных данных обычно называется <a href="/en-US/docs/AJAX">AJAX</a>, что означает <strong>"Asynchronous JavaScript And XML" </strong>(Асинхронный JavaScript и XML).</p> + +<h3 id="Чем_он_отличается">Чем он отличается?</h3> + +<p>Объект {{domxref("XMLHttpRequest")}} (XHR) DOM может создавать HTTP-запросы, отправлять их, и получать их результат. Исторически, {{domxref("XMLHttpRequest")}} был разработан для получения и отправки <a href="/en-US/docs/XML">XML</a> в качестве формата обмена, который со временем был заменен на <a href="/en-US/docs/JSON">JSON</a>. Но ни XML, ни JSON не вписываются в кодировку запроса данных формы. Данные формы (<code>application/x-www-form-urlencoded</code>) состоят из списка пар ключ/значение в кодировке URL. Для передачи бинарных данных, HTTP-запрос преобразуется в <code>multipart/form-data</code>.</p> + +<div class="blockIndicator note"> +<p><strong>Замечание</strong>: Сейчас <a href="/en-US/docs/Web/API/Fetch_API">Fetch API</a> часто используется вместо XHR — это современная, обновленная версия XHR, которая работает в похожем стиле, но имеет несколько преимуществ. Большая часть XHR-кода, которую вы увидете в этой статье можно заменить на Fetch.</p> +</div> + +<p>Если вы управляете фронтендом (кодом, который выполняется в браузере) и бкендом (кодом, который выполняется на стороне сервера), вы можете отправлять JSON/XML и обрабатывать их как хотите.</p> + +<p>Но если вы хотите использовать сторонний сервис, то вам необходимо отправлять данные в формате, который требуется сервису.</p> + +<p>Так как нам следует отправлять подобные данные? Ниже обписаны различные необходимые вам техники.</p> + +<h2 id="Отправка_данных_формы">Отправка данных формы</h2> + +<p>Есть три способа отправки данных формы:</p> + +<ul> + <li>Создание <code>XMLHttpRequest</code> вручную.</li> + <li>Использование самостоятельного <code>FormData</code> объекта.</li> + <li>Использование <code>FormData</code> связанного с <code><form></code> элементом.</li> +</ul> + +<p>Давайте рассмотрим их подробнее:</p> + +<h3 id="Создание_XMLHttpRequest_вручную">Создание XMLHttpRequest вручную</h3> + +<p>{{domxref("XMLHttpRequest")}} это самый безопасный и надежный способ создавать HTTPзапросы. Для отправки данных формы с помощью {{domxref("XMLHttpRequest")}}, подготовьте данные с помощью URL-кодирования, и соблюдайте специфику запросов данных формы.</p> + +<p>Посмотрите на пример:</p> + +<pre class="brush: html notranslate"><button>Click Me!</button></pre> + +<p>И на JavaScript:</p> + +<pre class="brush: js notranslate">const btn = document.querySelector('button'); + +function sendData( data ) { + console.log( 'Sending data' ); + + const XHR = new XMLHttpRequest(); + + let urlEncodedData = "", + urlEncodedDataPairs = [], + name; + + // Turn the data object into an array of URL-encoded key/value pairs. + for( name in data ) { + urlEncodedDataPairs.push( encodeURIComponent( name ) + '=' + encodeURIComponent( data[name] ) ); + } + + // Combine the pairs into a single string and replace all %-encoded spaces to + // the '+' character; matches the behaviour of browser form submissions. + urlEncodedData = urlEncodedDataPairs.join( '&' ).replace( /%20/g, '+' ); + + // Define what happens on successful data submission + XHR.addEventListener( 'load', function(event) { + alert( 'Yeah! Data sent and response loaded.' ); + } ); + + // Define what happens in case of error + XHR.addEventListener( 'error', function(event) { + alert( 'Oops! Something went wrong.' ); + } ); + + // Set up our request + XHR.open( 'POST', 'https://example.com/cors.php' ); + + // Add the required HTTP header for form data POST requests + XHR.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' ); + + // Finally, send our data. + XHR.send( urlEncodedData ); +} + +btn.addEventListener( 'click', function() { + sendData( {test:'ok'} ); +} ) +</pre> + +<p>Это результат:</p> + +<p>{{EmbedLiveSample("Building_an_XMLHttpRequest_manually", "100%", 50)}}</p> + +<div class="note"> +<p><strong>Note:</strong> This use of {{domxref("XMLHttpRequest")}} is subject to the {{glossary('same-origin policy')}} if you want to send data to a third party web site. For cross-origin requests, you'll need <a href="/en-US/docs/HTTP/Access_control_CORS">CORS and HTTP access control</a>.</p> +</div> + +<h3 id="Using_XMLHttpRequest_and_the_FormData_object">Using XMLHttpRequest and the FormData object</h3> + +<p>Building an HTTP request by hand can be overwhelming. Fortunately, the <a href="http://www.w3.org/TR/XMLHttpRequest/" rel="external">XMLHttpRequest specification</a> provides a newer, simpler way to handle form data requests with the {{domxref("XMLHttpRequest/FormData","FormData")}} object.</p> + +<p>The {{domxref("XMLHttpRequest/FormData","FormData")}} object can be used to build form data for transmission, or to get the data within a form element to manage how it's sent. Note that {{domxref("XMLHttpRequest/FormData","FormData")}} objects are "write only", which means you can change them, but not retrieve their contents.</p> + +<p>Using this object is detailed in <a href="/en-US/docs/DOM/XMLHttpRequest/FormData/Using_FormData_Objects">Using FormData Objects</a>, but here are two examples:</p> + +<h4 id="Using_a_standalone_FormData_object">Using a standalone FormData object</h4> + +<pre class="brush: html notranslate"><button>Click Me!</button></pre> + +<p>You should be familiar with that HTML sample. Now for the JavaScript:</p> + +<pre class="brush: js notranslate">const btn = document.querySelector('button'); + +function sendData( data ) { + const XHR = new XMLHttpRequest(), + FD = new FormData(); + + // Push our data into our FormData object + for( name in data ) { + FD.append( name, data[ name ] ); + } + + // Define what happens on successful data submission + XHR.addEventListener( 'load', function( event ) { + alert( 'Yeah! Data sent and response loaded.' ); + } ); + + // Define what happens in case of error + XHR.addEventListener(' error', function( event ) { + alert( 'Oops! Something went wrong.' ); + } ); + + // Set up our request + XHR.open( 'POST', 'https://example.com/cors.php' ); + + // Send our FormData object; HTTP headers are set automatically + XHR.send( FD ); +} + +btn.addEventListener( 'click', function() + { sendData( {test:'ok'} ); +} )</pre> + +<p>Here's the live result:</p> + +<p>{{EmbedLiveSample("Using_a_standalone_FormData_object", "100%", 50)}}</p> + +<h4 id="Using_FormData_bound_to_a_form_element">Using FormData bound to a form element</h4> + +<p>You can also bind a <code>FormData</code> object to an {{HTMLElement("form")}} element. This creates a <code>FormData</code> object that represents the data contained in the form.</p> + +<p>The HTML is typical:</p> + +<pre class="brush: html notranslate"><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></pre> + +<p>But JavaScript takes over the form:</p> + +<pre class="brush: js notranslate">window.addEventListener( "load", function () { + function sendData() { + const XHR = new XMLHttpRequest(); + + // Bind the FormData object and the form element + const FD = new FormData( form ); + + // Define what happens on successful data submission + XHR.addEventListener( "load", function(event) { + alert( event.target.responseText ); + } ); + + // Define what happens in case of error + XHR.addEventListener( "error", function( event ) { + alert( 'Oops! Something went wrong.' ); + } ); + + // Set up our request + XHR.open( "POST", "https://example.com/cors.php" ); + + // The data sent is what the user provided in the form + XHR.send( FD ); + } + + // Access the form element... + const form = document.getElementById( "myForm" ); + + // ...and take over its submit event. + form.addEventListener( "submit", function ( event ) { + event.preventDefault(); + + sendData(); + } ); +} );</pre> + +<p>Here's the live result:</p> + +<p>{{EmbedLiveSample("Using_FormData_bound_to_a_form_element", "100%", 50)}}</p> + +<p>You can even get more involved with the process by using the form's {{domxref("HTMLFormElement.elements", "elements")}} property to get a list of all of the data elements in the form and manually manage them one at a time. To learn more about that, see the example in {{SectionOnPage("/en-US/docs/Web/API/HTMLFormElement.elements", "Accessing the element list's contents")}}.</p> + +<h2 id="Dealing_with_binary_data">Dealing with binary data</h2> + +<p>If you use a {{domxref("XMLHttpRequest/FormData","FormData")}} object with a form that includes <code><input type="file"></code> widgets, the data will be processed automatically. But to send binary data by hand, there's extra work to do.</p> + +<p>There are many sources for binary data, including {{domxref("FileReader")}}, {{domxref("HTMLCanvasElement","Canvas")}}, and <a href="/en-US/docs/WebRTC/navigator.getUserMedia">WebRTC</a>. Unfortunately, some legacy browsers can't access binary data or require complicated workarounds. To learn more about the <code>FileReader</code> API, see <a href="/en-US/docs/Using_files_from_web_applications">Using files from web applications</a>.</p> + +<p>The least complicated way of sending binary data is by using {{domxref("XMLHttpRequest/FormData","FormData")}}'s <code>append()</code> method, demonstrated above. If you have to do it by hand, it's trickier.</p> + +<p>In the following example, we use the {{domxref("FileReader")}} API to access binary data and then build the multi-part form data request by hand:</p> + +<pre class="brush: html notranslate"><form id="theForm"> + <p> + <label for="theText">text data:</label> + <input id="theText" name="myText" value="Some text data" type="text"> + </p> + <p> + <label for="theFile">file data:</label> + <input id="theFile" name="myFile" type="file"> + </p> + <button>Send Me!</button> +</form></pre> + +<p>As you see, the HTML is a standard <code><form></code>. There's nothing magical going on. The "magic" is in the JavaScript:</p> + +<pre class="brush: js notranslate">// Because we want to access DOM nodes, +// we initialize our script at page load. +window.addEventListener( 'load', function () { + + // These variables are used to store the form data + const text = document.getElementById( "theText" ); + const file = { + dom : document.getElementById( "theFile" ), + binary : null + }; + + // Use the FileReader API to access file content + const reader = new FileReader(); + + // Because FileReader is asynchronous, store its + // result when it finishes to read the file + reader.addEventListener( "load", function () { + file.binary = reader.result; + } ); + + // At page load, if a file is already selected, read it. + if( file.dom.files[0] ) { + reader.readAsBinaryString( file.dom.files[0] ); + } + + // If not, read the file once the user selects it. + file.dom.addEventListener( "change", function () { + if( reader.readyState === FileReader.LOADING ) { + reader.abort(); + } + + reader.readAsBinaryString( file.dom.files[0] ); + } ); + + // sendData is our main function + function sendData() { + // If there is a selected file, wait it is read + // If there is not, delay the execution of the function + if( !file.binary && file.dom.files.length > 0 ) { + setTimeout( sendData, 10 ); + return; + } + + // To construct our multipart form data request, + // We need an XMLHttpRequest instance + const XHR = new XMLHttpRequest(); + + // We need a separator to define each part of the request + const boundary = "blob"; + + // Store our body request in a string. + let data = ""; + + // So, if the user has selected a file + if ( file.dom.files[0] ) { + // Start a new part in our body's request + data += "--" + boundary + "\r\n"; + + // Describe it as form data + data += 'content-disposition: form-data; ' + // Define the name of the form data + + 'name="' + file.dom.name + '"; ' + // Provide the real name of the file + + 'filename="' + file.dom.files[0].name + '"\r\n'; + // And the MIME type of the file + data += 'Content-Type: ' + file.dom.files[0].type + '\r\n'; + + // There's a blank line between the metadata and the data + data += '\r\n'; + + // Append the binary data to our body's request + data += file.binary + '\r\n'; + } + + // Text data is simpler + // Start a new part in our body's request + data += "--" + boundary + "\r\n"; + + // Say it's form data, and name it + data += 'content-disposition: form-data; name="' + text.name + '"\r\n'; + // There's a blank line between the metadata and the data + data += '\r\n'; + + // Append the text data to our body's request + data += text.value + "\r\n"; + + // Once we are done, "close" the body's request + data += "--" + boundary + "--"; + + // Define what happens on successful data submission + XHR.addEventListener( 'load', function( event ) { + alert( 'Yeah! Data sent and response loaded.' ); + } ); + + // Define what happens in case of error + XHR.addEventListener( 'error', function( event ) { + alert( 'Oops! Something went wrong.' ); + } ); + + // Set up our request + XHR.open( 'POST', 'https://example.com/cors.php' ); + + // Add the required HTTP header to handle a multipart form data POST request + XHR.setRequestHeader( 'Content-Type','multipart/form-data; boundary=' + boundary ); + + // And finally, send our data. + XHR.send( data ); + } + + // Access our form... + const form = document.getElementById( "theForm" ); + + // ...to take over the submit event + form.addEventListener( 'submit', function ( event ) { + event.preventDefault(); + sendData(); + } ); +} );</pre> + +<p>Here's the live result:</p> + +<p>{{EmbedLiveSample("Dealing_with_binary_data", "100%", 150)}}</p> + +<h2 id="Conclusion">Conclusion</h2> + +<p>Depending on the browser and the type of data you are dealing with, sending form data through JavaScript can be easy or difficult. The {{domxref("XMLHttpRequest/FormData","FormData")}} object is generally the answer, and you can use a <a href="https://github.com/jimmywarting/FormData">polyfill</a> for it on legacy browsers.</p> + +<h2 id="See_also">See also</h2> + +<h3 id="Learning_path">Learning path</h3> + +<ul> + <li><a href="/en-US/docs/Learn/HTML/Forms/Your_first_HTML_form">Your first HTML form</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/How_to_structure_an_HTML_form">How to structure an HTML form</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/The_native_form_widgets">The native form widgets</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/HTML5_input_types">HTML5 input types</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/Additional_form_controls">Additional form controls</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/UI_pseudo-classes">UI pseudo-classes</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/Styling_HTML_forms">Styling HTML forms</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/Form_validation">Form data validation</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/Sending_and_retrieving_form_data">Sending form data</a></li> +</ul> + +<h3 id="Advanced_Topics">Advanced Topics</h3> + +<ul> + <li><a href="/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript">Sending forms through JavaScript</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/How_to_build_custom_form_widgets">How to build custom form widgets</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/HTML_forms_in_legacy_browsers">HTML forms in legacy browsers</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/Advanced_styling_for_HTML_forms">Advanced styling for HTML forms</a></li> + <li><a href="/en-US/docs/Learn/HTML/Forms/Property_compatibility_table_for_form_widgets">Property compatibility table for form widgets</a></li> +</ul> |
