Skip to content

Implement FormData event #172

Open
Open

Description

@dfreedm

The FormData event allows Custom Elements, and other elements, to add new data to a Form element before submission.

More info: https://www.chromestatus.com/feature/5662230242656256

Activity

  1. self-assigned this
    on Jul 12, 2019
  2. YonatanKra commented on Aug 4, 2020

    @YonatanKra

    Note this is not supported in Safari

  3. stale commented on Aug 4, 2021

    @stale

    This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

  4. fernandopasik commented on Aug 4, 2021

    @fernandopasik

    would it be possible to avoid this one from going stale?

  5. bicknellr commented on Aug 4, 2021

    @bicknellr
    Collaborator

    I think these new comments should prevent the bot from closing this issue. FWIW, we're planning on coming back to this soon after we finish everything off for the next stable release of Lit.

  6. WickyNilliams commented on Sep 21, 2021

    @WickyNilliams

    I have been playing about with a very light polyfill for this. I have only attempted to support appending data since that is probably the 99% use case, especially for web components.

    The FormData class is basically available everywhere, the only missing part is the corresponding formdata event. So we can utilise this to keep the polyfill small. I add a hidden input to the form when data is appended.

    Here's what i have so far, including a feature test. It works in safari. I'm going to experiment with it further.

    function supportsFormDataEvent({ document }) {
      let isSupported = false
    
      const form = document.createElement("form")
      document.body.appendChild(form)
    
      form.addEventListener("submit", e => {
        e.preventDefault()
        // this dispatches formdata event in browsers that support it
        new FormData(e.target)
      })
      form.addEventListener("formdata", () => {
        isSupported = true
      })
    
      form.dispatchEvent(new Event("submit"))
      form.remove()
    
      return isSupported
    }
    
    export function polyfillFormData(win) {
      if (!win.FormData) {
        return
      }
      if (supportsFormDataEvent(win)) {
        return
      }
    
      class FormDataPoly extends FormData {
        constructor(form) {
          super(form)
          this.form = form
        }
    
        append(name, value) {
          super.append(name, value)
          let input = this.form.elements[name]
    
          if (!input) {
            input = document.createElement("input")
            input.type = "hidden"
            input.name = name
          }
    
          input.value = value
          this.form.appendChild(input)
        }
      }
    
      class FormDataEvent extends Event {
        constructor(form) {
          super("formdata")
          this.formData = new FormDataPoly(form)
        }
      }
    
      win.addEventListener("submit", e => {
        if (!e.defaultPrevented) {
          e.target.dispatchEvent(new FormDataEvent(e.target))
        }
      })
    }
  7. WickyNilliams commented on Sep 21, 2021

    @WickyNilliams

    I just noticed a polyfill has already been added to the repo, which is obviously more complete (at the cost of file size/invasiveness). Perhaps this issue can be closed if it is good to go :)

  8. WickyNilliams commented on Sep 29, 2021

    @WickyNilliams

    There were actually some small issues with the code I posted above. It didn't correctly handle anything but the first form submission, ending up with duplicate entries on subsequent submissions. Nor did it support manually calling new FormData(form) to gather form values outside of a submit event.

    The version below fixes these issues, and works well in my testing.

    Here's a gist of the code with an explicit license: https://gist.github.com/WickyNilliams/eb6a44075356ee504dd9491c5a3ab0be

    /* eslint-disable max-classes-per-file */
    
    class FormDataEvent extends Event {
      constructor(formData) {
        super("formdata")
        this.formData = formData
      }
    }
    
    class FormDataPolyfilled extends FormData {
      constructor(form) {
        super(form)
        this.form = form
        form.dispatchEvent(new FormDataEvent(this))
      }
    
      append(name, value) {
        let input = this.form.elements[name]
    
        if (!input) {
          input = document.createElement("input")
          input.type = "hidden"
          input.name = name
          this.form.appendChild(input)
        }
    
        // if the name already exists, there is already a hidden input in the dom
        // and it will have been picked up by FormData during construction.
        // in this case, we can't just blindly append() since that will result in two entries.
        // nor can we blindly delete() the entry, since there can be multiple entries per name (e.g. checkboxes).
        // so we must carefully splice out the old value, and add back in the new value
        if (this.has(name)) {
          const entries = this.getAll(name)
          const index = entries.indexOf(input.value)
    
          if (index !== -1) {
            entries.splice(index, 1)
          }
    
          entries.push(value)
          this.set(name, entries)
        } else {
          super.append(name, value)
        }
    
        input.value = value
      }
    }
    
    function supportsFormDataEvent({ document }) {
      let isSupported = false
    
      const form = document.createElement("form")
      document.body.appendChild(form)
    
      form.addEventListener("submit", e => {
        e.preventDefault()
        // this dispatches formdata event in browsers that support it
        new FormData(e.target) // eslint-disable-line no-new
      })
    
      form.addEventListener("formdata", () => {
        isSupported = true
      })
    
      form.dispatchEvent(new Event("submit", { cancelable: true }))
      form.remove()
    
      return isSupported
    }
    
    function polyfillFormData(win) {
      if (!win.FormData || supportsFormDataEvent(win)) {
        return
      }
    
      window.FormData = FormDataPolyfilled
      win.addEventListener("submit", e => {
        if (!e.defaultPrevented) {
          // eslint-disable-next-line no-new
          new FormData(e.target)
        }
      })
    }
    
    polyfillFormData(window)
  9. stale commented on Jan 21, 2023

    @stale

    This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

  10. TechQuery commented on Jan 23, 2023

    @TechQuery

    Any update in 2023 ?

    New version of React official document is dropping Class components, Web components is the right way to write Class components, hope to improve these details.

  11. stale commented on Apr 26, 2025

    @stale

    This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions