formData in web-components

Hello, I made a web component:
class Toggle extends HTMLElement {
#toggle = /*html*/`
<input type="checkbox" ${this.checked ? 'checked' : ''} />
`

constructor() {
super()

const shadow = this.attachShadow({mode: 'open'})
shadow.innerHTML = this.#toggle
this.internals = this.attachInternals()

shadow.querySelector('input').addEventListener('input', ({currentTarget}) => {
this.checked = currentTarget.checked
})
}

static formAssociated = true;

get checked() {
return this.hasAttribute('checked');
}

set checked(isChecked) {
this.internals.setFormValue(this.getAttribute('value'))
isChecked ? this.setAttribute('checked', '') : this.removeAttribute('checked')
}
}
customElements.define('my-toggle', GfToggle)
class Toggle extends HTMLElement {
#toggle = /*html*/`
<input type="checkbox" ${this.checked ? 'checked' : ''} />
`

constructor() {
super()

const shadow = this.attachShadow({mode: 'open'})
shadow.innerHTML = this.#toggle
this.internals = this.attachInternals()

shadow.querySelector('input').addEventListener('input', ({currentTarget}) => {
this.checked = currentTarget.checked
})
}

static formAssociated = true;

get checked() {
return this.hasAttribute('checked');
}

set checked(isChecked) {
this.internals.setFormValue(this.getAttribute('value'))
isChecked ? this.setAttribute('checked', '') : this.removeAttribute('checked')
}
}
customElements.define('my-toggle', GfToggle)
This is the html:
<form>
<my-toggle name="bla" value="foo"></my-toggle>
</form>
<form>
<my-toggle name="bla" value="foo"></my-toggle>
</form>
And it works, the form element recognizes my-toggle as a member of the form. But console.log(new FormData(form)) gives me an empty FormData object. Why?
17 Replies
ἔρως
ἔρως9mo ago
instead of adding html, try creating the element using document.createElement but honestly, if you want to make a toggle, i would just do a label with styles
Wonderbear
Wonderbear9mo ago
I need more than one element to make a toggle, thats why I wanted to use a custom element
ἔρως
ἔρως9mo ago
or you can just set the appearence: none on the checkbox, and now you can style with a ::before and ::after
Wonderbear
Wonderbear9mo ago
Why?
ἔρως
ἔρως9mo ago
and its all 1 element because adding html like that may have unintended side effects does it have? i dont know
Wonderbear
Wonderbear9mo ago
I use innerHTML because I want to be able to write readable html instead of calling dozens of methods But I'll try using :before and :after and get rid of the component
get out...!
get out...!9mo ago
it will be more convient and this type of work
ἔρως
ἔρως9mo ago
trust me, it is a lot easier you can set the checkbox to have appearance: none with a height, width, border-radius and background the ::before can have a height that's 100% height - 2x the width of the border and have 50% border radius and then you can just transition the background and position of the ::before
get out...!
get out...!9mo ago
here is the code <!DOCTYPE html> <html> <style> .myinput { appearance: none; display: inline-block; position: relative; width: 20px; height: 20px; border: 2px solid gray; border-radius: 4px; transition: background-color, border-color ease-in 0.2s; cursor: pointer; } .myinput::before { content: ''; display: none; position: absolute; top: 1px; left: 50%; translate: -50% 0; height: 60%; width: 6px; border-right: 2px solid white; border-bottom: 2px solid white; transform: rotate(45deg); transition: display ease-in 0.2s; } .myinput:hover { border-color: lightGray; } .myinput:checked { background: green; } .myinput:checked::before { display: inline-block;
} </style> <body> <form> <input class="myinput" type="checkbox"/> </form> </body> </html> you can do more using html data attribute
ἔρως
ἔρως9mo ago
display doesnt transition: it jumps from "none" to "block"
Wonderbear
Wonderbear9mo ago
yup I already almost have it
ἔρως
ἔρως9mo ago
and this way, you dont have to try to shoehorn your custom element into the formdata
Wonderbear
Wonderbear9mo ago
Thanks, using a web-component was, as so often, the wrong way
get out...!
get out...!9mo ago
my bad, Instead I use opacity
Wonderbear
Wonderbear9mo ago
What do you need opacity for anyway?
label.toggle {
display: flex;
}
input[type="checkbox"].toggle {
appearance: none;
height: .8rem;
width: 2rem;
background-color: var(--gray);
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 99px;
position: relative;
margin: .3rem 0;
margin-right: .3rem;
transition: .25s;
}
input[type="checkbox"].toggle:focus {
border: none;
}
input[type="checkbox"].toggle::after {
content: "";
position: absolute;
display: block;
height: 1rem;
width: 1rem;
border-radius: 99px;
background-color: var(--darkgray);
box-shadow: var(--shadow-sm);
transition: .25s;
box-sizing: border-box;
}
input[type="checkbox"].toggle:checked::after {
transform: translateX(100%);
background-color: #000;
}
label.toggle {
display: flex;
}
input[type="checkbox"].toggle {
appearance: none;
height: .8rem;
width: 2rem;
background-color: var(--gray);
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 99px;
position: relative;
margin: .3rem 0;
margin-right: .3rem;
transition: .25s;
}
input[type="checkbox"].toggle:focus {
border: none;
}
input[type="checkbox"].toggle::after {
content: "";
position: absolute;
display: block;
height: 1rem;
width: 1rem;
border-radius: 99px;
background-color: var(--darkgray);
box-shadow: var(--shadow-sm);
transition: .25s;
box-sizing: border-box;
}
input[type="checkbox"].toggle:checked::after {
transform: translateX(100%);
background-color: #000;
}
ἔρως
ἔρως9mo ago
transitioning from hidden to visible also, avoid using transition without properties that means that everything will transition, when the css loads including width, height and other properties that cause layout shifting
get out...!
get out...!9mo ago
good advise