Polyfill HTML5 form attribute (for input fields)

后端 未结 6 845
悲&欢浪女
悲&欢浪女 2020-12-19 03:44

This is the markup I use:


...
相关标签:
6条回答
  • 2020-12-19 04:01

    after reading thru the docs of webshim it seems it has a polyfill for that.

    http://afarkas.github.io/webshim/demos/demos/webforms.html

    0 讨论(0)
  • 2020-12-19 04:04

    I wrote this polyfill to emulate such feature by duplicating fields upon form submission, tested in IE6 and it worked fine.

    (function($) {
      /**
       * polyfill for html5 form attr
       */
    
      // detect if browser supports this
      var sampleElement = $('[form]').get(0);
      var isIE11 = !(window.ActiveXObject) && "ActiveXObject" in window;
      if (sampleElement && window.HTMLFormElement && (sampleElement.form instanceof HTMLFormElement || sampleElement instanceof window.HTMLFormElement) && !isIE11) {
        // browser supports it, no need to fix
        return;
      }
    
      /**
       * Append a field to a form
       *
       */
      $.fn.appendField = function(data) {
        // for form only
        if (!this.is('form')) return;
    
        // wrap data
        if (!$.isArray(data) && data.name && data.value) {
          data = [data];
        }
    
        var $form = this;
    
        // attach new params
        $.each(data, function(i, item) {
          $('<input/>')
            .attr('type', 'hidden')
            .attr('name', item.name)
            .val(item.value).appendTo($form);
        });
    
        return $form;
      };
    
      /**
       * Find all input fields with form attribute point to jQuery object
       * 
       */
      $('form[id]').submit(function(e) {
        // serialize data
        var data = $('[form='+ this.id + ']').serializeArray();
        // append data to form
        $(this).appendField(data);
      }).each(function() {
        var form = this,
          $fields = $('[form=' + this.id + ']');
    
        $fields.filter('button, input').filter('[type=reset],[type=submit]').click(function() {
          var type = this.type.toLowerCase();
          if (type === 'reset') {
            // reset form
            form.reset();
            // for elements outside form
            $fields.each(function() {
              this.value = this.defaultValue;
              this.checked = this.defaultChecked;
            }).filter('select').each(function() {
              $(this).find('option').each(function() {
                this.selected = this.defaultSelected;
              });
            });
          } else if (type.match(/^submit|image$/i)) {
            $(form).appendField({name: this.name, value: this.value}).submit();
          }
        });
      });
    
    
    })(jQuery);
    
    0 讨论(0)
  • 2020-12-19 04:05

    I improved patstuart's polyfill, such that:

    • a form can now be submitted several times, e.g. when using the target attribute (external fields were duplicated previously)

    • reset buttons now work properly

    Here it is:

    (function($) {
    /**
     * polyfill for html5 form attr
     */
    
    // detect if browser supports this
    var SAMPLE_FORM_NAME = "html-5-polyfill-test";
    var sampleForm = $("<form id='" + SAMPLE_FORM_NAME + "'/>");
    var sampleFormAndHiddenInput = sampleForm.add($("<input type='hidden' form='" + SAMPLE_FORM_NAME + "'/>"));     
    sampleFormAndHiddenInput.prependTo('body'); 
    var sampleElementFound = sampleForm[0].elements[0];
    sampleFormAndHiddenInput.remove();
    if (sampleElementFound) {
        // browser supports it, no need to fix
        return;
    }
    
    /**
     * Append a field to a form
     *
     */
    var CLASS_NAME_POLYFILL_MARKER = "html-5-polyfill-form-attr-marker";
    $.fn.appendField = function(data) {
      // for form only
      if (!this.is('form')) return;
    
      // wrap data
      if (!$.isArray(data) && data.name && data.value) {
        data = [data];
      }
    
      var $form = this;
    
      // attach new params
      $.each(data, function(i, item) {
        $('<input/>')
          .attr('type', 'hidden')
          .attr('name', item.name)
          .attr('class', CLASS_NAME_POLYFILL_MARKER)
          .val(item.value).appendTo($form);
      });
    
      return $form;
    };
    
    /**
     * Find all input fields with form attribute point to jQuery object
     * 
     */
    $('form[id]').submit(function(e, origSubmit) {
      // clean up form from last submit
      $('.'+CLASS_NAME_POLYFILL_MARKER, this).remove();
      // serialize data
      var data = $('[form='+ this.id + ']').serializeArray();
      // add data from external submit, if needed:
      if (origSubmit && origSubmit.name)
        data.push({name: origSubmit.name, value: origSubmit.value})
      // append data to form
      $(this).appendField(data);
    })
    
    //submit and reset behaviour
    $('button[type=reset], input[type=reset]').click(function() {
      //extend reset buttons to fields with matching form attribute
      // reset form
      var formId = $(this).attr("form");
      var formJq = $('#'+formId);
      if (formJq.length)
        formJq[0].reset();
      // for elements outside form
      if (!formId)
        formId = $(this).closest("form").attr("id");
      $fields = $('[form=' + formId + ']');
      $fields.each(function() {
        this.value = this.defaultValue;
        this.checked = this.defaultChecked;
      }).filter('select').each(function() {
        $(this).find('option').each(function() {
          this.selected = this.defaultSelected;
        });
      });
    });
    $('button[type=submit], input[type=submit], input[type=image]').click(function() {
      var formId = $(this).attr("form") || $(this).closest("form").attr("id");
      $('#'+formId).trigger('submit', this);  //send clicked submit as extra parameter
    });
    

    })(jQuery);

    0 讨论(0)
  • 2020-12-19 04:07

    The polyfill above doesn't take into account the Edge browser. I have amended it to use feature detection, which I have tested in IE7+, Edge, Firefox (mobile/desktop), Chrome (mobile/desktop), Safari (mobile/desktop), and Android browser 4.0.

    (function($) {
        /**
         * polyfill for html5 form attr
         */
    
        // detect if browser supports this
        var SAMPLE_FORM_NAME = "html-5-polyfill-test";
        var sampleForm = $("<form id='" + SAMPLE_FORM_NAME + "'/>");
        var sampleFormAndHiddenInput = sampleForm.add($("<input type='hidden' form='" + SAMPLE_FORM_NAME + "'/>"));     
        sampleFormAndHiddenInput.prependTo('body'); 
        var sampleElementFound = sampleForm[0].elements[0];
        sampleFormAndHiddenInput.remove();
        if (sampleElementFound) {
            // browser supports it, no need to fix
            return;
        }
    
        /**
         * Append a field to a form
         *
         */
        $.fn.appendField = function(data) {
          // for form only
          if (!this.is('form')) return;
    
          // wrap data
          if (!$.isArray(data) && data.name && data.value) {
            data = [data];
          }
    
          var $form = this;
    
          // attach new params
          $.each(data, function(i, item) {
            $('<input/>')
              .attr('type', 'hidden')
              .attr('name', item.name)
              .val(item.value).appendTo($form);
          });
    
          return $form;
        };
    
        /**
         * Find all input fields with form attribute point to jQuery object
         * 
         */
        $('form[id]').submit(function(e) {
          // serialize data
          var data = $('[form='+ this.id + ']').serializeArray();
          // append data to form
          $(this).appendField(data);
        }).each(function() {
          var form = this,
            $fields = $('[form=' + this.id + ']');
    
          $fields.filter('button, input').filter('[type=reset],[type=submit]').click(function() {
            var type = this.type.toLowerCase();
            if (type === 'reset') {
              // reset form
              form.reset();
              // for elements outside form
              $fields.each(function() {
                this.value = this.defaultValue;
                this.checked = this.defaultChecked;
              }).filter('select').each(function() {
                $(this).find('option').each(function() {
                  this.selected = this.defaultSelected;
                });
              });
            } else if (type.match(/^submit|image$/i)) {
              $(form).appendField({name: this.name, value: this.value}).submit();
            }
          });
        });
    
    
      })(jQuery);
    
    0 讨论(0)
  • 2020-12-19 04:07

    I take some time to send an update for this polyfill because it doesn't work with MS Edge.

    I add 2 line to fix it :

          var isEdge = navigator.userAgent.indexOf("Edge");
          if (sampleElement && window.HTMLFormElement && sampleElement.form instanceof HTMLFormElement && !isIE11 && isEdge == -1) {
            // browser supports it, no need to fix
            return;
          }
    

    UPDATE: Edge now support it: https://caniuse.com/#feat=form-attribute

    0 讨论(0)
  • 2020-12-19 04:11

    I made a vanilla JavaScript polyfill based on the above polyfills and uploaded it on GitHub: https://github.com/Ununnilium/form-attribute-polyfill. I also added a custom event to handle the case when submit is processed by JavaScript and not directly by the browser. I tested the code only shortly with IE 11, so please check it yourself before use. The polling should maybe be replaced by a more efficient detection function.

    function browserNeedsPolyfill() {
        var TEST_FORM_NAME = "form-attribute-polyfill-test";
        var testForm = document.createElement("form");
        testForm.setAttribute("id", TEST_FORM_NAME);
        testForm.setAttribute("type", "hidden");
        var testInput = document.createElement("input");
        testInput.setAttribute("type", "hidden");
        testInput.setAttribute("form", TEST_FORM_NAME);
        testForm.appendChild(testInput);
        document.body.appendChild(testInput);
        document.body.appendChild(testForm);
        var sampleElementFound = testForm.elements.length === 1;
        document.body.removeChild(testInput);
        document.body.removeChild(testForm);
        return !sampleElementFound;
    }
    
    // Ideas from jQuery form attribute polyfill https://stackoverflow.com/a/26696165/2372674
    function executeFormPolyfill() {
        function appendDataToForm(data, form) {
            Object.keys(data).forEach(function(name) {
                var inputElem = document.createElement("input");
                inputElem.setAttribute("type", "hidden");
                inputElem.setAttribute("name", name);
                inputElem.value = data[name];
                form.appendChild(inputElem);
            });
        }
    
        var forms = document.body.querySelectorAll("form[id]");
        Array.prototype.forEach.call(forms, function (form) {
            var fields = document.querySelectorAll('[form="' + form.id + '"]');
            var dataFields = [];
            Array.prototype.forEach.call(fields, function (field) {
                if (field.disabled === false && field.hasAttribute("name")) {
                    dataFields.push(field);
                }
            });
            Array.prototype.forEach.call(fields, function (field) {
                if (field.type === "reset") {
                    field.addEventListener("click", function () {
                        form.reset();
                        Array.prototype.forEach.call(dataFields, function (dataField) {
                            if (dataField.nodeName === "SELECT") {
                                Array.prototype.forEach.call(dataField.querySelectorAll('option'), function (option) {
                                    option.selected = option.defaultSelected;
                                });
                            } else {
                                dataField.value = dataField.defaultValue;
                                dataField.checked = dataField.defaultChecked;
                            }
                        });
                    });
                } else if (field.type === "submit" || field.type === "image") {
                    field.addEventListener("click", function () {
                        var obj = {};
                        obj[field.name] = field.value;
                        appendDataToForm(obj, form);
                        form.dispatchEvent(eventToDispatch);
                    });
                }
            });
            form.addEventListener("submit", function () {
                var data = {};
                Array.prototype.forEach.call(dataFields, function (dataField) {
                    data[dataField.name] = dataField.value;
                });
                appendDataToForm(data, form);
            });
        });
    }
    
    // Poll for new forms and execute polyfill for them
    function detectedNewForms() {
        var ALREADY_DETECTED_CLASS = 'form-already-detected';
        var newForms = document.querySelectorAll('form:not([class="' + ALREADY_DETECTED_CLASS + '"])');
        if (newForms.length !== 0) {
            Array.prototype.forEach.call(newForms, function (form) {
                form.className += ALREADY_DETECTED_CLASS;
            });
            executeFormPolyfill();
        }
        setTimeout(detectedNewForms, 100);
    }
    
    
    // Source: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
    function polyfillCustomEvent() {
        if (typeof window.CustomEvent === "function") {
            return false;
        }
    
        function CustomEvent(event, params) {
            params = params || {bubbles: false, cancelable: false, detail: undefined};
            var evt = document.createEvent('CustomEvent');
            evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
            return evt;
        }
    
        CustomEvent.prototype = window.Event.prototype;
        window.CustomEvent = CustomEvent;
    }
    
    if (browserNeedsPolyfill()) {
        polyfillCustomEvent();   // IE is missing CustomEvent
    
        // This workaround is needed if submit is handled by JavaScript instead the browser itself
        // Source: https://stackoverflow.com/a/35155789/2372674
        var eventToDispatch = new CustomEvent("submit", {"bubbles": true, "cancelable": true});
        detectedNewForms();   // Poll for new forms and execute form attribute polyfill for new forms
    }
    
    0 讨论(0)
提交回复
热议问题