Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement FormData event #172

Open
dfreedm opened this issue Jul 12, 2019 · 9 comments
Open

Implement FormData event #172

dfreedm opened this issue Jul 12, 2019 · 9 comments

Comments

@dfreedm
Copy link
Contributor

dfreedm commented Jul 12, 2019

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

@YonatanKra
Copy link

Note this is not supported in Safari

@stale
Copy link

stale bot commented Aug 4, 2021

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.

@stale stale bot added the wontfix label Aug 4, 2021
@fernandopasik
Copy link

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

@bicknellr
Copy link
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.

@stale stale bot removed the wontfix label Aug 4, 2021
@WickyNilliams
Copy link

WickyNilliams commented Sep 21, 2021

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))
    }
  })
}

@WickyNilliams
Copy link

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 :)

@WickyNilliams
Copy link

WickyNilliams commented Sep 29, 2021

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)

@stale
Copy link

stale bot commented Jan 21, 2023

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.

@stale stale bot added the wontfix label Jan 21, 2023
@TechQuery
Copy link

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.

@stale stale bot removed the wontfix label Jan 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants