In Search of a Reimplementation of FormData

This is a chat of mine with GitHub Copilot. Nice job GitHub!

movahhedi: Explain line by line exactly what each line of the code from the jquery source code defining the serialize functions for forms does:

jQuery.fn.extend( {
    serialize: function() {
        return jQuery.param( this.serializeArray() );
    },
    serializeArray: function() {
        return this.map( function() {

            // Can add propHook for "elements" to filter or add form elements
            var elements = jQuery.prop( this, "elements" );
            return elements ? jQuery.makeArray( elements ) : this;
        } ).filter( function() {
            var type = this.type;

            // Use .is( ":disabled" ) so that fieldset[disabled] works
            return this.name && !jQuery( this ).is( ":disabled" ) &&
                rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
                ( this.checked || !rcheckableType.test( type ) );
        } ).map( function( _i, elem ) {
            var val = jQuery( this ).val();

            if ( val == null ) {
                return null;
            }

            if ( Array.isArray( val ) ) {
                return jQuery.map( val, function( val ) {
                    return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                } );
            }

            return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
        } ).get();
    }
} );

GitHub Copilot: This code is defining two functions, serialize and serializeArray, as methods on jQuery objects. Here's a line-by-line breakdown:

jQuery.fn.extend( {

This line is extending the jQuery prototype (also accessible as jQuery.fn), which means adding new methods to it that will be available on all jQuery objects.

serialize: function() {
    return jQuery.param( this.serializeArray() );
},

The serialize method is defined here. It calls this.serializeArray() to get an array representation of the form data, and then passes that array to jQuery.param to convert it into a URL-encoded string.

serializeArray: function() {

The serializeArray method is defined here. It returns an array representation of the form data.

return this.map( function() {

This line starts a map operation on the jQuery object (which represents a set of DOM elements). For each element, the provided function is called.

var elements = jQuery.prop( this, "elements" );
return elements ? jQuery.makeArray( elements ) : this;

This code gets the "elements" property of the current DOM element (which should be a form), converts it to a true array if it exists, and returns it. If the "elements" property doesn't exist, it returns the original DOM element.

} ).filter( function() {

This line starts a filter operation on the result of the map. For each element, the provided function is called, and if it returns true, the element is included in the final result.

var type = this.type;
return this.name && !jQuery( this ).is( ":disabled" ) &&
    rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
    ( this.checked || !rcheckableType.test( type ) );

This code checks several conditions to determine whether the current form element should be included in the serialized data. It must have a name, not be disabled, be a submittable type of element, not be a submitter type of element, and either be checked or not be a checkable type of element.

} ).map( function( _i, elem ) {

This line starts another map operation on the result of the filter. For each element, the provided function is called.

var val = jQuery( this ).val();
if ( val == null ) {
    return null;
}

This code gets the value of the current form element. If the value is null, it returns null.

if ( Array.isArray( val ) ) {
    return jQuery.map( val, function( val ) {
        return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
    } );
}

If the value is an array (which it will be for multi-selects and checkboxes), it maps each item in the array to an object with name and value properties. The value is also replaced any CRLF pairs with a single newline character.

return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };

If the value is not an array, it returns an object with name and value properties. The value is also replaced any CRLF pairs with a single newline character.

} ).get();

This line calls get on the result of the map to convert it from a jQuery object back into a true array.

}
} );

These lines close the serializeArray function, the object passed to jQuery.fn.extend, and the call to jQuery.fn.extend, respectively.

movahhedi: explain the following lines fully and extensively and bring the references from the jquery cource code:

            // Use .is( ":disabled" ) so that fieldset[disabled] works
            return this.name && !jQuery( this ).is( ":disabled" ) &&
                rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
                ( this.checked || !rcheckableType.test( type ) );
                `

GitHub Copilot: This line of code is part of the filter function in the serializeArray method. It's used to determine which form elements should be included in the serialized data. Let's break it down:

return this.name && !jQuery( this ).is( ":disabled" ) &&
    rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
    ( this.checked || !rcheckableType.test( type ) );
  1. this.name: This checks if the current form element has a name attribute. In HTML forms, the name attribute is used to identify the form data for each input element when the data is submitted. If an element doesn't have a name, it won't be included in the serialized data.

  2. !jQuery( this ).is( ":disabled" ): This checks if the current form element is not disabled. The :disabled selector in jQuery matches all elements that are disabled. So, jQuery( this ).is( ":disabled" ) will return true if the element is disabled, and false otherwise. The ! operator negates this, so the overall condition is true if the element is not disabled.

  3. rsubmittable.test( this.nodeName ): This checks if the current form element's node name matches the rsubmittable regular expression. In the jQuery source code, rsubmittable is defined as /^(?:input|select|textarea|keygen)/i, which matches input, select, textarea, and keygen elements. The i flag makes the match case-insensitive.

  4. !rsubmitterTypes.test( type ): This checks if the current form element's type does not match the rsubmitterTypes regular expression. In the jQuery source code, rsubmitterTypes is defined as /^submit|button|image|reset|file$/i, which matches submit, button, image, reset, and file types. The i flag makes the match case-insensitive.

  5. this.checked || !rcheckableType.test( type ): This checks if the current form element is checked or if its type does not match the rcheckableType regular expression. In the jQuery source code, rcheckableType is defined as /^(?:checkbox|radio)$/i, which matches checkbox and radio types. The i flag makes the match case-insensitive. This condition ensures that unchecked checkboxes and radio buttons are not included in the serialized data.

So, overall, this line of code is checking that the current form element has a name, is not disabled, is an input, select, textarea, or keygen element, is not a submit, button, image, reset, or file type, and is either checked or not a checkbox or radio type. If all these conditions are met, the element will be included in the serialized data.

movahhedi: compare this serializeArray function with the FormData Constructor extensively. compare each of the elements` values when used with each. which one supports elements with duplicate names? how does it handle them?

GitHub Copilot: The serializeArray function in jQuery and the FormData constructor in vanilla JavaScript are both used to create a set of key/value pairs representing form fields and their values. However, they handle data differently and have different features.

  1. Data Format:

    • serializeArray: Returns an array of objects where each object has a name and value property. For example: [{name: 'username', value: 'John'}, {name: 'password', value: '1234'}].
    • FormData: Returns a FormData object. You can use the entries, keys, values, or getAll methods to retrieve the data. The entries method, for example, returns an iterator allowing you to loop through all key/value pairs.
  2. File Inputs:

    • serializeArray: Does not support file inputs. If a form contains input[type=file], the file input will not be included in the serialized data.
    • FormData: Supports file inputs. If a form contains input[type=file], the file input will be included in the FormData object.
  3. Disabled Inputs:

    • serializeArray: Does not include disabled inputs in the serialized data.
    • FormData: Includes disabled inputs in the FormData object.
  4. Handling Elements with Duplicate Names:

    • serializeArray: Supports elements with duplicate names. If a form contains multiple elements with the same name, serializeArray will include all of them in the serialized data.
    • FormData: Also supports elements with duplicate names. If a form contains multiple elements with the same name, FormData will include all of them. You can use the getAll(name) method to retrieve all values with the same name.
  5. Handling Checkbox and Radio Inputs:

    • serializeArray: Only includes checkboxes and radio buttons that are checked.
    • FormData: Also only includes checkboxes and radio buttons that are checked.
  6. Handling Select-multiple Inputs:

    • serializeArray: If a select-multiple input has multiple options selected, serializeArray will include an object for each selected option in the serialized data.
    • FormData: If a select-multiple input has multiple options selected, FormData will include a key/value pair for each selected option.

In conclusion, while serializeArray and FormData have some similarities, FormData provides more features and flexibility, especially when dealing with file inputs and disabled inputs. However, the choice between the two often depends on the specific requirements of your project.

movahhedi: how exactly do each handle arrays?

GitHub Copilot: Both serializeArray and FormData handle arrays differently, especially when it comes to form elements like multiple select and checkboxes.

  1. serializeArray:

    • If a form contains a multiple select box or checkboxes with the same name, serializeArray will create an object for each selected option or checked box. Each object will have the same name (the name of the select box or checkboxes) and a value corresponding to the value of the selected option or checked box.
    • For example, if a form contains a multiple select box named colors with options red, blue, and green selected, serializeArray will return: [{name: 'colors', value: 'red'}, {name: 'colors', value: 'blue'}, {name: 'colors', value: 'green'}].
  2. FormData:

    • FormData also supports multiple values for the same name. If a form contains a multiple select box or checkboxes with the same name, FormData will include a key/value pair for each selected option or checked box.
    • However, unlike serializeArray, FormData does not return an array directly. You can use the getAll(name) method to retrieve all values with the same name as an array. For example, if a form contains a multiple select box named colors with options red, blue, and green selected, formData.getAll('colors') will return: ['red', 'blue', 'green'].

In conclusion, while both serializeArray and FormData support multiple values for the same name, they represent these values differently. serializeArray includes them in the main array it returns, while FormData stores them internally and you need to use the getAll method to retrieve them as an array.

0
Subscribe to my newsletter

Read articles from Shahab Movahhedi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shahab Movahhedi
Shahab Movahhedi

Senior Developer, Designer, Technologist! ๐Ÿ‡ฎ๐Ÿ‡ท