[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-requestsubmit-form-validation-fix":44,"search-suggestions":234,"path-membership-requestsubmit-form-validation-fix":274,"related-requestsubmit-form-validation-fix":275,"code:html:true:1bnz1zp":410,"code:js:true:667qe7":411,"code:js:true:1cv5kw7":412,"code:js:true:yufbkt":413,"comments-01a16761-d787-7313-a57c-88979dbb3a59":414},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":75,"playground":77,"body":79,"bodyMd":217,"seo":218,"translationGroupId":221,"series":48,"podcastUrl":48,"verticalId":5,"thread":222,"assessments":224,"translations":230,"quiz":232},"01a16761-d787-7313-a57c-88979dbb3a59","requestsubmit-form-validation-fix","Stop Calling form.submit(). Use requestSubmit() Instead.",null,"A Save button living outside the \u003Cform> tag, wired to form.submit(), looks like it works — until a blank required field sails through and the page does a full reload your fetch handler never saw coming. requestSubmit() is the method that actually clicks the button for you.","\u002Fmedia\u002Fcovers\u002Frequestsubmit-form-validation-fix.png",6,"2026-10-28T06:10:26.806Z",31,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"javascript","JavaScript",[64,66,69,72],{"slug":61,"name":65,"color":48},"Javascript",{"slug":67,"name":68,"color":48},"webdev","Webdev",{"slug":70,"name":71,"color":48},"html","Html",{"slug":73,"name":74,"color":48},"programming","Programming",{"assessments":76},1,{"slug":46,"title":78},"form.submit() vs requestSubmit() — interactive playground",{"blocks":80,"version":76},[81,85,88,91,95,98,103,106,111,114,117,121,124,127,130,133,136,139,143,146,149,152,155,158,161,164,168,171,174,177,180,183,186,189,192,195,198,201,204,207,210],{"id":82,"html":83,"type":84},"b1","\u003Cp>The bug report said &quot;the modal saves blank data sometimes.&quot; Not always — just sometimes, and only for one field: the user&#39;s email. QA could reproduce it maybe one click in five, and only if they clicked Save fast, before the field had a chance to go red.\u003C\u002Fp>","paragraph",{"id":86,"html":87,"type":84},"b2","\u003Cp>Here&#39;s the part that should have been impossible: the input had \u003Ccode>required\u003C\u002Fcode> on it. HTML form validation is supposed to catch an empty required field before anything goes anywhere. And yet, once in a while, it didn&#39;t — the page would flash white for a quarter second, like it had just reloaded, and the record would land in the database with an empty string where the email should be.\u003C\u002Fp>",{"id":89,"html":90,"type":84},"b3","\u003Cp>It had reloaded. That was the whole bug, once you knew to look for it.\u003C\u002Fp>",{"id":92,"html":93,"text":93,"type":94,"level":31},"b4","The button that lived outside the form","heading",{"id":96,"html":97,"type":84},"b5","\u003Cp>The modal in question followed a pattern you&#39;ve probably built yourself: the form fields sit in the body of the dialog, but the &quot;Cancel&quot; \u002F &quot;Save&quot; buttons live in a shared footer component that&#39;s reused across every modal in the app — rendered as a sibling, not a child, of the \u003Ccode>&lt;form&gt;\u003C\u002Fcode> element.\u003C\u002Fp>",{"id":99,"code":100,"type":101,"language":70,"highlight":102},"b6","\u003Cform id=\"profile-form\">\n  \u003Cinput name=\"email\" type=\"email\" required \u002F>\n  \u003C!-- more fields -->\n\u003C\u002Fform>\n\n\u003C!-- the footer is a separate component, outside the form -->\n\u003Cdiv class=\"modal-footer\">\n  \u003Cbutton type=\"button\" id=\"cancel-btn\">Cancel\u003C\u002Fbutton>\n  \u003Cbutton type=\"button\" id=\"save-btn\">Save\u003C\u002Fbutton>\n\u003C\u002Fdiv>","code",[],{"id":104,"html":105,"type":84},"b7","\u003Cp>You can&#39;t put a \u003Ccode>type=&quot;submit&quot;\u003C\u002Fcode> button outside a \u003Ccode>&lt;form&gt;\u003C\u002Fcode> and have it submit that form just by proximity — the DOM doesn&#39;t work that way. So whoever wired this up reached for the obvious-looking fix: give the form an id, grab a reference to it, and submit it from the button&#39;s click handler.\u003C\u002Fp>",{"id":107,"code":108,"type":101,"language":109,"highlight":110},"b8","document.getElementById(\"profile-form\").addEventListener(\"submit\", (e) => {\n  e.preventDefault();\n  saveProfile(new FormData(e.target)); \u002F\u002F a fetch() call\n});\n\ndocument.getElementById(\"save-btn\").addEventListener(\"click\", () => {\n  document.getElementById(\"profile-form\").submit();\n});","js",[],{"id":112,"html":113,"type":84},"b9","\u003Cp>This reads like it should work. It even \u003Cem>looks\u003C\u002Fem> like it works most of the time — click Save with a filled-in form, and the profile saves. The bug only shows up on the path nobody tests by hand: clicking Save on an invalid form, fast, before you&#39;ve noticed the field is empty.\u003C\u002Fp>",{"id":115,"html":116,"type":84},"b10","\u003Cp>\u003Cstrong>Before you scroll: what do you think \u003Ccode>form.submit()\u003C\u002Fcode> actually skips?\u003C\u002Fstrong> It&#39;s not a trick question — the answer is in the method&#39;s own name, once you say it out loud.\u003C\u002Fp>",{"id":118,"html":119,"text":120,"type":94,"level":31},"b11","\u003Ccode>.submit()\u003C\u002Fcode> doesn&#39;t click anything — it just submits",".submit() doesn't click anything — it just submits",{"id":122,"html":123,"type":84},"b12","\u003Cp>\u003Ccode>form.submit()\u003C\u002Fcode> is one of the oldest methods on the DOM, older than \u003Ccode>addEventListener\u003C\u002Fcode> itself, and it does exactly what it says: it submits the form, as raw HTTP, right now. What it does \u003Cem>not\u003C\u002Fem> do is simulate a user clicking a submit button — and that distinction is the entire bug.\u003C\u002Fp>",{"id":125,"html":126,"type":84},"b13","\u003Cp>Clicking a real \u003Ccode>&lt;button type=&quot;submit&quot;&gt;\u003C\u002Fcode> triggers a sequence: the browser runs the form&#39;s constraint validation (\u003Ccode>required\u003C\u002Fcode>, \u003Ccode>pattern\u003C\u002Fcode>, \u003Ccode>type=&quot;email&quot;\u003C\u002Fcode>, \u003Ccode>minlength\u003C\u002Fcode>, all of it); if anything fails, the browser blocks the submission and shows the native &quot;Please fill out this field&quot; bubble; only if everything passes does it fire a cancelable \u003Ccode>submit\u003C\u002Fcode> event, which is what your \u003Ccode>addEventListener(&quot;submit&quot;, ...)\u003C\u002Fcode> handler is listening for.\u003C\u002Fp>",{"id":128,"html":129,"type":84},"b14","\u003Cp>\u003Ccode>form.submit()\u003C\u002Fcode> skips every step of that. No validation runs. No \u003Ccode>submit\u003C\u002Fcode> event fires. The form just submits — as a real, browser-driven navigation to its \u003Ccode>action\u003C\u002Fcode> URL, using its \u003Ccode>method\u003C\u002Fcode> — which is exactly the &quot;page flash&quot; QA saw. The \u003Ccode>saveProfile\u003C\u002Fcode> fetch call never ran, because the event it&#39;s attached to never fired. The browser did its own, uncontrolled submission underneath your SPA&#39;s feet.\u003C\u002Fp>",{"id":131,"html":132,"type":84},"b15","\u003Cp>That&#39;s not a bug in \u003Ccode>.submit()\u003C\u002Fcode>. It&#39;s the method doing its one job — bypass everything and submit — which happens to be the one job you never actually wanted.\u003C\u002Fp>",{"id":134,"html":135,"text":135,"type":94,"level":31},"b16","The fix is a different method, not a different pattern",{"id":137,"html":138,"type":84},"b17","\u003Cp>\u003Ccode>requestSubmit()\u003C\u002Fcode> exists for precisely this situation: you need to trigger a form&#39;s submission from code, and you want it to behave exactly like clicking a real submit button would — because that&#39;s literally what it does.\u003C\u002Fp>",{"id":140,"code":141,"type":101,"language":109,"highlight":142},"b18","document.getElementById(\"save-btn\").addEventListener(\"click\", () => {\n  document.getElementById(\"profile-form\").requestSubmit();\n});",[],{"id":144,"html":145,"type":84},"b19","\u003Cp>One word changed. The behavior is now correct: an empty \u003Ccode>email\u003C\u002Fcode> field blocks submission and shows the native validation bubble, a filled one runs constraint validation, passes, and fires a real \u003Ccode>submit\u003C\u002Fcode> event — which your existing \u003Ccode>e.preventDefault()\u003C\u002Fcode> handler catches, exactly as if someone had clicked a submit button that was actually inside the form.\u003C\u002Fp>",{"id":147,"html":148,"type":84},"b20","\u003C!-- playground:start -->",{"id":150,"html":151,"text":151,"type":94,"level":31},"b21","🎮 Try it yourself",{"id":153,"html":154,"type":84},"b22","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Frequestsubmit-form-validation-fix\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":156,"html":157,"type":84},"b23","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":159,"html":160,"type":84},"b24","\u003C!-- playground:end -->",{"id":162,"html":163,"type":84},"b25","\u003Cp>\u003Ccode>requestSubmit()\u003C\u002Fcode> also takes an optional argument: a specific \u003Ccode>&lt;button&gt;\u003C\u002Fcode> to submit &quot;as.&quot; Pass the actual button that was clicked, and the submission carries that button&#39;s \u003Ccode>name\u003C\u002Fcode>\u002F\u003Ccode>value\u003C\u002Fcode> pair and honors its \u003Ccode>formaction\u003C\u002Fcode> \u002F \u003Ccode>formmethod\u003C\u002Fcode> overrides — the same as a real click would, if you have more than one submit button with different behavior (a &quot;Save&quot; vs. &quot;Save and publish,&quot; say).\u003C\u002Fp>",{"id":165,"code":166,"type":101,"language":109,"highlight":167},"b26","form.requestSubmit(saveAndPublishButton);",[],{"id":169,"html":170,"type":84},"b27","\u003Cp>One sharp edge here, and it&#39;s the kind that only shows up once: the button you pass has to actually be a submit button associated with \u003Cem>that\u003C\u002Fem> form — a descendant of it, or linked via \u003Ccode>form=&quot;profile-form&quot;\u003C\u002Fcode>. Pass a \u003Ccode>type=&quot;button&quot;\u003C\u002Fcode> that isn&#39;t a submit button at all, and \u003Ccode>requestSubmit()\u003C\u002Fcode> throws immediately; pass a real submit button that belongs to a \u003Cem>different\u003C\u002Fem> form, and it throws too, just under a different error name. Either way it&#39;s a loud, synchronous failure, not a silent no-op — that&#39;s louder than \u003Ccode>.submit()\u003C\u002Fcode> ever gave you, and louder is better here: you find out in testing, not from a QA ticket three sprints later.\u003C\u002Fp>",{"id":172,"html":173,"text":173,"type":94,"level":31},"b28","The part worth double-checking: is this safe to ship everywhere?",{"id":175,"html":176,"type":84},"b29","\u003Cp>Yes, and that&#39;s worth saying plainly, because plenty of the newer form and DOM APIs aren&#39;t. \u003Ccode>requestSubmit()\u003C\u002Fcode> shipped in Chrome in 2019 and Firefox followed in 2020 — Safari was the holdout, catching up in Safari 16 in September 2022, which is the point it became Baseline: available in every evergreen browser, no flag and no polyfill required. There&#39;s no fallback section needed in this post, which is rarer than it should be.\u003C\u002Fp>",{"id":178,"html":179,"type":84},"b30","\u003Cp>The only real caveat is the one most people hit the opposite way: \u003Ccode>.submit()\u003C\u002Fcode> still exists, does nothing wrong \u003Cem>by its own definition\u003C\u002Fem>, and will keep compiling and &quot;working&quot; forever — nothing errors, nothing warns, your linter stays silent. The only signal you get that you reached for the wrong method is a user complaint that doesn&#39;t reproduce consistently, which is exactly the shape of bug that&#39;s expensive to track down and cheap to prevent.\u003C\u002Fp>",{"id":181,"html":182,"type":84},"b31","\u003C!-- quiz:start -->",{"id":184,"html":185,"text":185,"type":94,"level":31},"b32","🧠 Test yourself",{"id":187,"html":188,"type":84},"b33","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Frequestsubmit-form-validation-fix\u002Fquiz\">Take the 7-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":190,"html":191,"type":84},"b34","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":193,"html":194,"type":84},"b35","\u003C!-- quiz:end -->",{"id":196,"html":197,"type":84},"b36","\u003Cp>The fix here was one word. Finding it meant noticing that &quot;the page flashed and reloaded&quot; is \u003Ccode>.submit()\u003C\u002Fcode>&#39;s actual, documented behavior wearing a disguise — not a flaky bug, not a race condition, just a method doing precisely what it says and nothing you asked for. Anywhere your code calls \u003Ccode>.submit()\u003C\u002Fcode> to trigger a form from outside it — a footer button, a keyboard shortcut, a wizard&#39;s &quot;Next&quot; step — that&#39;s \u003Ccode>requestSubmit()\u003C\u002Fcode>&#39;s job, not \u003Ccode>.submit()\u003C\u002Fcode>&#39;s.\u003C\u002Fp>",{"id":199,"html":200,"type":84},"b37","\u003Cp>Have you shipped a &quot;phantom reload&quot; bug like this one? What gave it away first — the validation that didn&#39;t run, or the request that never showed up in the network tab?\u003C\u002Fp>",{"id":202,"type":203},"b38","divider",{"id":205,"html":206,"type":84},"b39","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":208,"html":209,"type":84},"b40","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":211,"type":212,"items":213,"ordered":18},"b41","list",[214,215,216],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","The bug report said \"the modal saves blank data sometimes.\" Not always — just sometimes, and only for one field: the user's email. QA could reproduce it maybe one click in five, and only if they clicked Save fast, before the field had a chance to go red.\n\nHere's the part that should have been impossible: the input had `required` on it. HTML form validation is supposed to catch an empty required field before anything goes anywhere. And yet, once in a while, it didn't — the page would flash white for a quarter second, like it had just reloaded, and the record would land in the database with an empty string where the email should be.\n\nIt had reloaded. That was the whole bug, once you knew to look for it.\n\n## The button that lived outside the form\n\nThe modal in question followed a pattern you've probably built yourself: the form fields sit in the body of the dialog, but the \"Cancel\" \u002F \"Save\" buttons live in a shared footer component that's reused across every modal in the app — rendered as a sibling, not a child, of the `\u003Cform>` element.\n\n```html\n\u003Cform id=\"profile-form\">\n  \u003Cinput name=\"email\" type=\"email\" required \u002F>\n  \u003C!-- more fields -->\n\u003C\u002Fform>\n\n\u003C!-- the footer is a separate component, outside the form -->\n\u003Cdiv class=\"modal-footer\">\n  \u003Cbutton type=\"button\" id=\"cancel-btn\">Cancel\u003C\u002Fbutton>\n  \u003Cbutton type=\"button\" id=\"save-btn\">Save\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n```\n\nYou can't put a `type=\"submit\"` button outside a `\u003Cform>` and have it submit that form just by proximity — the DOM doesn't work that way. So whoever wired this up reached for the obvious-looking fix: give the form an id, grab a reference to it, and submit it from the button's click handler.\n\n```js\ndocument.getElementById(\"profile-form\").addEventListener(\"submit\", (e) => {\n  e.preventDefault();\n  saveProfile(new FormData(e.target)); \u002F\u002F a fetch() call\n});\n\ndocument.getElementById(\"save-btn\").addEventListener(\"click\", () => {\n  document.getElementById(\"profile-form\").submit();\n});\n```\n\nThis reads like it should work. It even *looks* like it works most of the time — click Save with a filled-in form, and the profile saves. The bug only shows up on the path nobody tests by hand: clicking Save on an invalid form, fast, before you've noticed the field is empty.\n\n**Before you scroll: what do you think `form.submit()` actually skips?** It's not a trick question — the answer is in the method's own name, once you say it out loud.\n\n## `.submit()` doesn't click anything — it just submits\n\n`form.submit()` is one of the oldest methods on the DOM, older than `addEventListener` itself, and it does exactly what it says: it submits the form, as raw HTTP, right now. What it does *not* do is simulate a user clicking a submit button — and that distinction is the entire bug.\n\nClicking a real `\u003Cbutton type=\"submit\">` triggers a sequence: the browser runs the form's constraint validation (`required`, `pattern`, `type=\"email\"`, `minlength`, all of it); if anything fails, the browser blocks the submission and shows the native \"Please fill out this field\" bubble; only if everything passes does it fire a cancelable `submit` event, which is what your `addEventListener(\"submit\", ...)` handler is listening for.\n\n`form.submit()` skips every step of that. No validation runs. No `submit` event fires. The form just submits — as a real, browser-driven navigation to its `action` URL, using its `method` — which is exactly the \"page flash\" QA saw. The `saveProfile` fetch call never ran, because the event it's attached to never fired. The browser did its own, uncontrolled submission underneath your SPA's feet.\n\nThat's not a bug in `.submit()`. It's the method doing its one job — bypass everything and submit — which happens to be the one job you never actually wanted.\n\n## The fix is a different method, not a different pattern\n\n`requestSubmit()` exists for precisely this situation: you need to trigger a form's submission from code, and you want it to behave exactly like clicking a real submit button would — because that's literally what it does.\n\n```js\ndocument.getElementById(\"save-btn\").addEventListener(\"click\", () => {\n  document.getElementById(\"profile-form\").requestSubmit();\n});\n```\n\nOne word changed. The behavior is now correct: an empty `email` field blocks submission and shows the native validation bubble, a filled one runs constraint validation, passes, and fires a real `submit` event — which your existing `e.preventDefault()` handler catches, exactly as if someone had clicked a submit button that was actually inside the form.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Frequestsubmit-form-validation-fix\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n`requestSubmit()` also takes an optional argument: a specific `\u003Cbutton>` to submit \"as.\" Pass the actual button that was clicked, and the submission carries that button's `name`\u002F`value` pair and honors its `formaction` \u002F `formmethod` overrides — the same as a real click would, if you have more than one submit button with different behavior (a \"Save\" vs. \"Save and publish,\" say).\n\n```js\nform.requestSubmit(saveAndPublishButton);\n```\n\nOne sharp edge here, and it's the kind that only shows up once: the button you pass has to actually be a submit button associated with *that* form — a descendant of it, or linked via `form=\"profile-form\"`. Pass a `type=\"button\"` that isn't a submit button at all, and `requestSubmit()` throws immediately; pass a real submit button that belongs to a *different* form, and it throws too, just under a different error name. Either way it's a loud, synchronous failure, not a silent no-op — that's louder than `.submit()` ever gave you, and louder is better here: you find out in testing, not from a QA ticket three sprints later.\n\n## The part worth double-checking: is this safe to ship everywhere?\n\nYes, and that's worth saying plainly, because plenty of the newer form and DOM APIs aren't. `requestSubmit()` shipped in Chrome in 2019 and Firefox followed in 2020 — Safari was the holdout, catching up in Safari 16 in September 2022, which is the point it became Baseline: available in every evergreen browser, no flag and no polyfill required. There's no fallback section needed in this post, which is rarer than it should be.\n\nThe only real caveat is the one most people hit the opposite way: `.submit()` still exists, does nothing wrong *by its own definition*, and will keep compiling and \"working\" forever — nothing errors, nothing warns, your linter stays silent. The only signal you get that you reached for the wrong method is a user complaint that doesn't reproduce consistently, which is exactly the shape of bug that's expensive to track down and cheap to prevent.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 7-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Frequestsubmit-form-validation-fix\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\nThe fix here was one word. Finding it meant noticing that \"the page flashed and reloaded\" is `.submit()`'s actual, documented behavior wearing a disguise — not a flaky bug, not a race condition, just a method doing precisely what it says and nothing you asked for. Anywhere your code calls `.submit()` to trigger a form from outside it — a footer button, a keyboard shortcut, a wizard's \"Next\" step — that's `requestSubmit()`'s job, not `.submit()`'s.\n\nHave you shipped a \"phantom reload\" bug like this one? What gave it away first — the validation that didn't run, or the request that never showed up in the network tab?\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":47,"canonical":219,"description":220},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Frequestsubmit-form-validation-fix","A Save button living outside the \u003Cform> tag, wired to form.submit(), looks like it works — until a blank required field sails through and the page does a full reload your fetch han","01a16761-d787-7313-a57c-8d9ff810094d",{"id":223,"locked":18},"01a16761-d7cd-7288-8a03-eacbfed7b825",[225],{"id":226,"slug":46,"title":227,"_count":228},"01a16761-d7fe-759a-901f-fc780bed47d3","form.submit() vs requestSubmit()",{"questions":229},7,[231],{"locale":13,"slug":46},{"id":226,"slug":46,"title":227,"_count":233,"questionCount":229},{"questions":229},[235,237,239,243,247,251,255,259,263,265,268,272],{"slug":67,"name":68,"articles":236},157,{"slug":61,"name":65,"articles":238},129,{"slug":240,"name":241,"articles":242},"frontend","Frontend",89,{"slug":244,"name":245,"articles":246},"tutorial","Tutorial",61,{"slug":248,"name":249,"articles":250},"css","Css",46,{"slug":252,"name":253,"articles":254},"performance","Performance",26,{"slug":256,"name":257,"articles":258},"typescript","Typescript",22,{"slug":260,"name":261,"articles":262},"react","React",19,{"slug":73,"name":74,"articles":264},15,{"slug":266,"name":267,"articles":264},"browser","Browser",{"slug":269,"name":270,"articles":271},"node","Node",13,{"slug":70,"name":71,"articles":273},11,[],{"items":276,"meta":409},[277,289,309,328,347,370,388],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":278,"likeCount":19,"commentCount":19,"author":279,"vertical":280,"topic":281,"tags":282,"_count":287,"playground":288,"hasQuiz":17,"hasPlayground":17},32,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[283,284,285,286],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":46},{"id":290,"slug":291,"title":292,"subtitle":48,"excerpt":293,"coverUrl":294,"locale":13,"readingMinutes":271,"publishedAt":295,"viewCount":296,"likeCount":19,"commentCount":19,"author":297,"vertical":298,"topic":299,"tags":302,"_count":307,"playground":308,"hasQuiz":17,"hasPlayground":17},"01a11b9f-f19f-7057-9a83-d09cd7fb120e","vue-weekly-vfor-directive-unmounted-hook","Vue v-for: The Directive Unmounted Hook Bug, Fixed in 3.5.41","A custom directive's unmounted hook could silently skip firing inside a keyed v-for list. The directive unmounted hook bug, Vue's real fix, and a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fvue-weekly-vfor-directive-unmounted-hook.png","2026-10-26T05:39:58.065Z",136,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":300,"name":301},"vue","Vue",[303,304,305,306],{"slug":300,"name":301,"color":48},{"slug":61,"name":65,"color":48},{"slug":244,"name":245,"color":48},{"slug":67,"name":68,"color":48},{"assessments":76},{"slug":291},{"id":310,"slug":311,"title":312,"subtitle":48,"excerpt":313,"coverUrl":314,"locale":13,"readingMinutes":315,"publishedAt":316,"viewCount":317,"likeCount":19,"commentCount":19,"author":318,"vertical":319,"topic":320,"tags":321,"_count":326,"playground":327,"hasQuiz":17,"hasPlayground":17},"01a15d15-24b7-7510-b76c-15fc8d3b2f58","requestanimationframe-scroll-throttling","You're Debouncing Scroll Wrong. requestAnimationFrame Fixes It.","Debouncing a scroll handler makes it wait until scrolling stops — which is why your sticky header always lags half a beat. requestAnimationFrame fixes the lag by syncing updates to the browser's own paint clock instead of a timer.","\u002Fmedia\u002Fcovers\u002Frequestanimationframe-scroll-throttling.png",5,"2026-10-26T05:39:27.008Z",142,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[322,323,324,325],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":252,"name":253,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":311},{"id":329,"slug":330,"title":331,"subtitle":48,"excerpt":332,"coverUrl":333,"locale":13,"readingMinutes":229,"publishedAt":334,"viewCount":335,"likeCount":19,"commentCount":19,"author":336,"vertical":337,"topic":338,"tags":340,"_count":345,"playground":346,"hasQuiz":17,"hasPlayground":17},"01a157ee-b1c1-7777-8aa6-ef7aa4ef6bd3","typescript-as-const-assertions","My Reducer's Switch Stopped Being Exhaustive. `as const` Fixed It.","Object and array literals widen to `string` and `number` the moment you write them, which quietly breaks discriminated unions and exhaustive switch checks. `as const` keeps the literal types — but it also freezes the value, which bites in its own way.","\u002Fmedia\u002Fcovers\u002Ftypescript-as-const-assertions.png","2026-10-25T19:53:06.537Z",119,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":256,"name":339},"TypeScript",[341,342,343,344],{"slug":256,"name":257,"color":48},{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":330},{"id":348,"slug":349,"title":350,"subtitle":48,"excerpt":351,"coverUrl":352,"locale":13,"readingMinutes":353,"publishedAt":354,"viewCount":355,"likeCount":19,"commentCount":19,"author":356,"vertical":357,"topic":358,"tags":361,"_count":368,"playground":369,"hasQuiz":17,"hasPlayground":17},"01a16761-c18b-70aa-93d5-4f4ab1a25a88","nuxt-weekly-nuxt-server-import-boundary","Nuxt 4.6 nuxt\u002Fserver: Why Mixing h3 Imports Throws NUXT_E8012","Nuxt 4.6 added a new nuxt\u002Fserver import source alongside h3 — mix the two in one handler and you hit NUXT_E8012. The real boundary, plus a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-nuxt-server-import-boundary.png",16,"2026-10-25T19:52:35.714Z",113,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":359,"name":360},"nuxt","Nuxt",[362,363,366,367],{"slug":359,"name":360,"color":48},{"slug":364,"name":365,"color":48},"backend","Backend",{"slug":61,"name":65,"color":48},{"slug":244,"name":245,"color":48},{"assessments":76},{"slug":349},{"id":371,"slug":372,"title":373,"subtitle":48,"excerpt":374,"coverUrl":375,"locale":13,"readingMinutes":315,"publishedAt":376,"viewCount":377,"likeCount":19,"commentCount":19,"author":378,"vertical":379,"topic":380,"tags":381,"_count":386,"playground":387,"hasQuiz":17,"hasPlayground":17},"01a14e81-eaa5-700a-a6f6-3be4ffe47c2b","usesyncexternalstore-tearing-fix","Your Custom Hooks Tear in React 18. `useSyncExternalStore` Fixes It.","A useState + useEffect hook that subscribes to window size, localStorage, or a WebSocket looks fine until React 18's concurrent rendering shows two parts of your UI two different values at once. useSyncExternalStore is the hook built to stop that.","\u002Fmedia\u002Fcovers\u002Fusesyncexternalstore-tearing-fix.png","2026-10-23T06:11:03.306Z",198,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":260,"name":261},[382,383,384,385],{"slug":260,"name":261,"color":48},{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":240,"name":241,"color":48},{"assessments":76},{"slug":372},{"id":389,"slug":390,"title":391,"subtitle":48,"excerpt":392,"coverUrl":393,"locale":13,"readingMinutes":315,"publishedAt":394,"viewCount":395,"likeCount":19,"commentCount":19,"author":396,"vertical":397,"topic":398,"tags":400,"_count":407,"playground":408,"hasQuiz":17,"hasPlayground":17},"01a149cb-71f3-71ba-bb39-4f894fa2f1a8","css-individual-transform-properties","Stop Jamming Translate, Rotate, and Scale Into One `transform`","A CSS :hover scale and a JS drag handler both write to `transform` — and the moment JS sets it, the hover rule silently stops working, no error, no warning. The `translate`, `rotate`, and `scale` properties exist so this never happens.","\u002Fmedia\u002Fcovers\u002Fcss-individual-transform-properties.png","2026-10-22T06:10:24.373Z",197,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":248,"name":399},"CSS",[401,402,403,404],{"slug":248,"name":249,"color":48},{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":405,"name":406,"color":48},"animation","Animation",{"assessments":76},{"slug":390},{"page":76,"perPage":229,"total":238,"totalPages":262},"\u003Cdiv class=\"shj shj-lang-html shj-multiline\" data-lang=\"html\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan> \u003Cspan class=\"shj-class\">id\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"profile-form\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">input\u003C\u002Fspan> \u003Cspan class=\"shj-class\">name\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"email\"\u003C\u002Fspan> \u003Cspan class=\"shj-class\">type\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"email\"\u003C\u002Fspan> \u003Cspan class=\"shj-class\">required\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-cmnt\">&lt;!-- more fields --&gt;\u003C\u002Fspan>\n\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-cmnt\">&lt;!-- the footer is a separate component, outside the form --&gt;\u003C\u002Fspan>\n\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan> \u003Cspan class=\"shj-class\">class\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"modal-footer\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan> \u003Cspan class=\"shj-class\">type\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"button\"\u003C\u002Fspan> \u003Cspan class=\"shj-class\">id\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"cancel-btn\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>Cancel\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan> \u003Cspan class=\"shj-class\">type\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"button\"\u003C\u002Fspan> \u003Cspan class=\"shj-class\">id\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"save-btn\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>Save\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">document\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getElementById\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"profile-form\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"submit\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>e\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  e\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">preventDefault\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-func\">saveProfile\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">FormData\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>e\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>target\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F a fetch() call\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\ndocument\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getElementById\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"save-btn\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"click\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  document\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getElementById\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"profile-form\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">submit\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">document\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getElementById\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"save-btn\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"click\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  document\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getElementById\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"profile-form\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">requestSubmit\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-oneline\" data-lang=\"js\">form\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">requestSubmit\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>saveAndPublishButton\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":415},[]]