The “Show hint” button reveals some text for a moment, then the page reloads. A developer may look for a JavaScript error even though the cause is HTML: an auxiliary button inside a form behaves as a submit button. A form button’s type, not its colour or label, determines the browser’s default action. On a local training page, we will reproduce an unwanted GET request and remove it while preserving the real search button.
The example creates no enquiries, orders or payments and does not connect to a live website. Use only the fictional search string demo. Do not reproduce the initial mistake in a production form: submitting it may have real consequences. The English training labels and identifiers below remain unchanged so you can easily compare them with the code and test results.
Observe navigation separately from the hint
Create a separate empty test directory and a new form-demo.html file containing the markup below. You need a modern browser, a text editor and an existing Python 3 interpreter only for the local static server; no third-party packages or administrator privileges are required. Do not put configuration files, private documents or symbolic links to other data in this directory. Do not overwrite an existing project file.
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Button type demo</title>
<body>
<form method="get">
<label for="q">Query</label>
<input id="q" name="q" value="demo">
<button id="hint-toggle" aria-controls="hint"
aria-expanded="false">Show hint</button>
<button type="submit">Search</button>
<p id="hint" hidden>Use a short search phrase.</p>
</form>
<script>
const toggle = document.getElementById("hint-toggle");
toggle.addEventListener("click", () => {
document.getElementById("hint").hidden = false;
toggle.setAttribute("aria-expanded", "true");
});
</script>
</body>
</html>
This is deliberately the initial, incorrect version: Show hint has no type. The click handler reveals the paragraph and changes aria-expanded, but does not cancel the button’s default action. In this ordinary form, the button without type is a submit button. The precise conditions and attribute values are defined by the HTML standard for button. Do not apply that conclusion mechanically to specialised buttons with other declarative attributes.
In the terminal, move specifically to the new test directory and run the command. It starts a local server bound only to 127.0.0.1 and serves files from the current directory. This is a temporary process, not a hosting configuration. If the port is occupied, choose another free number and update it in the browser address too. Do not expand the binding to all network interfaces.
python3 -m http.server 8765 --bind 127.0.0.1
Open http://127.0.0.1:8765/form-demo.html and click Show hint. The expected sign of the initial bug is an address containing q=demo and another document load; the hint is hidden again afterwards. The Network panel or local server log can show the GET request containing that parameter. The static server only returns the file: it does not implement searching. We are checking form submission itself, not search results.
Give the auxiliary button an explicit type
Replace only the Show hint element in your file with this fragment, including its contents and closing tag. Leave the input, hint and script unchanged. The identifiers hint-toggle and hint are examples; in your own component, replace them consistently in HTML, aria-controls and JavaScript. Do not change the real Search button’s type to button: it must submit the form.
<button id="hint-toggle" type="button"
aria-controls="hint" aria-expanded="false">Show hint</button>
Save the file and reopen it without a query parameter. Click Show hint: the text should remain visible, aria-expanded should become true and the address should not change. Then click Search: this action should now add q=demo to the address. If neither button submits the form anymore, that is not success: you have removed the intended workflow along with the bug.
Use type="button" for an auxiliary action and type="submit" for submission. The purpose of type="reset" is different: it restores fields to their initial values, which does not necessarily make them empty. It is not a substitute for fixing accidental submission and may discard text the user entered. This exercise needs no reset button at all; do not add one merely to make the interface symmetrical.
Do not hide the problem with global interception
Do not add preventDefault to every click on the page as a universal repair. That makes ordinary links and genuine submission harder to understand. When a form really is submitted through JavaScript, its submit handler, data validation and error handling need a separate design. This exercise implements no such transport: it fixes one auxiliary button’s semantics without disabling the standard submission path.
Check the result with a keyboard: Tab to Show hint and activate it with Space or Enter, then test Search separately. Press Enter in the text field and compare the behaviour with your form’s requirements. Do not treat a single mouse click as a complete test. After a reload, the hint starts hidden: that is the example’s deliberate initial state, not a mechanism for preserving a choice.
If a button appears outside the form’s visible boundaries, check its form attribute: it can associate the element with a form by identifier. Inspect the actual DOM after the template or component has run, not just the source fragment in an editor. Do not nest one form inside another; incorrect structure can change element associations and make the reason for submission unclear.
Transfer the change only after control scenarios pass
Before changing a live template, preserve its current version and test the fix in a test environment. Record separate outcomes for the auxiliary action, intentional submission and keyboard use. To roll back, restore the agreed template version rather than disabling form protection. The correct type does not replace server-side checks of permissions or data, or protection against repeated operations.
After the exercise, stop the server with Ctrl+C and remove only the training files you created. The Python documentation warns that http.server is not intended for production serving. In conclusion, an explicit button type separates an auxiliary action from submission; two different control scenarios demonstrate the result. For broader testing, see checking an enquiry form before launch (in Ukrainian). AriZone can help locate unwanted submissions without experimenting with real enquiries.
Leave a comment