A friendly, step-by-step page that shows how to make buttons that do things — simple button actions in HTML. Learn when to use submit, button, or reset, why Enter submits, why links (<a>) are best for going to a URL, how formaction switches destinations, and how to label icon buttons.
クリックで何かが起きるボタンの作り方を、submit/button/resetの違い・Enterで送信される理由・URL移動は<a>推奨・formactionで送信先切り替え・アイコンボタンの名前付けまで、短いコードとコツで迷わず実装できるようにまとめたページ。
HTML ボタンで実行できるアクション(button action)を中心に、よく使う操作やURLへの移動方法をわかりやすく整理しています。
<button> 要素は、ユーザーがクリックすることでアクションを実行するためのボタンを作成します。テキストやアイコンなど、ボタンに表示したい内容をタグの中に書きます。
HTML
<button>Click me</button>
この例では「Click me」という文字が表示されたボタンが作成されます。クリック時の動作は後述の JavaScript で自由に追加できます。
ボタンの動作を決めます。基本は次の3つ:
"submit"(デフォルト)type を省略すると submit になります。"button""reset"<form> 内の入力を、初期値(value など)に戻します。超重要:フォーム内の <button> は 書かないと submit です。クリック専用のときは type="button" を必ず付けましょう。
必須入力を1つ用意し、送信すると 新しいタブでクエリ(?name=...&agree=on)が見えて「送れた」ことを確認できます。サーバーがなくても動きます。
HTML
<form action="" method="get" target="_blank">
<label>お名前
<input name="name" required>
</label>
<label>
<input type="checkbox" name="agree" required> 利用規約に同意
</label>
<button type="submit">送信</button>
</form>
送信後に、新しいタブでアドレスバーのURLを見ると、https://yugien.xyz/web/html/button.html?name=Yugien&agree=on#typeSubmit となっており、?name=Yugien&agree=on が増えているので、正しく送信できていることが確認できます。
ポイント:
required の検証はブラウザが自動で実施(JSなし)。同じフォーム内にあっても type="button" なら送信されません。ここでは「確認メッセージのオン/オフ」を切り替える例です。
HTML + JavaScript
<form id="demoButtonForm" action="" method="get" target="_blank">
<label>メール
<input name="email" type="email" placeholder="[email protected]">
</label>
<!-- 送信しないプレビューボタン -->
<button type="button" id="previewBtn">プレビュー表示/非表示</button>
<!-- 実際の送信ボタン -->
<button type="submit">送信</button>
<p id="previewBox" hidden>プレビュー:入力内容を確認しました。</p>
</form>
<script>
(function () {
const btn = document.getElementById('previewBtn');
const box = document.getElementById('previewBox');
if (btn && box) {
btn.addEventListener('click', function () {
box.hidden = !box.hidden; // その場で表示のON/OFFを切り替え
});
}
})();
</script>
ポイント:
type="button" を使う。type を書かないと submit になり、クリックだけで送信される事故の元。value などで指定した「初期値」に戻します。使いどころは限られるため、誤操作防止の工夫(確認や目立たせない配置)をしましょう。
HTML + JavaScript(確認ダイアログ付き)
<form id="demoResetForm">
<label>お名前
<input name="name" value="山田太郎">
</label>
<label>色の好み
<select name="color">
<option>赤</option>
<option selected>青</option>
<option>緑</option>
</select>
</label>
<button type="reset" id="resetBtn">入力をクリア</button>
</form>
<script>
(function () {
const form = document.getElementById('demoResetForm');
const btn = document.getElementById('resetBtn');
if (form && btn) {
btn.addEventListener('click', function (e) {
if (!confirm('入力を初期状態に戻します。よろしいですか?')) {
e.preventDefault(); // リセットをキャンセル
}
});
}
})();
</script>
注意:reset は同じ <form> 内だけに効きます。他のフォームは対象外です。
まとめ:
type="submit"(省略可)。検証や submit イベントはブラウザが面倒を見ます。type="button"。Enter の暗黙送信からも守れるので、フォーム内の「開く/閉じる」「計算」などは必ずこれ。type="reset"。誤操作に注意し、必要なときだけ使う(確認ダイアログ等)。ボタンを無効化し、クリックできないようにします。グレー表示になり「同意する」にチェックを入れるまで押せないボタンなどで利用されます。
HTML
<button disabled>送信</button>
注意:aria-disabled="true" は見た目や読み上げでは「無効」に聞こえますが、実際にはクリックできます。押せないようにするには disabled を使うか、click イベントで event.preventDefault() などの制御が必要です。
補足:disabled にしたボタンは フォーカスできず送信対象にもなりません(name/value も送られません)。
フォーム送信時に、ボタンが押されたときの名前と値をサーバーに送ります。
HTML
<form action="/submit" method="post">
<button type="submit" name="submit" value="clicked">送信</button>
</form>
補足:サーバーに送られるのは「実際に送信に使われた ボタン」の name=value です。値を確実に扱いたい場合は、value を 明示 しましょう。
a、操作は button(button action の基本)ページ遷移(リンク移動)には <a>、操作(送信・開閉など)には <button> を使います。
見た目が似ていても、役割が異なるため適切なタグを使い分けましょう。
注意:リンクに role="button" を付けてボタンのように見せる方法は、実務ではアンチパターンです。<a href> から href を外したり既定動作を止めてしまうと、中クリックや長押しメニュー、URLコピー、履歴(戻る)など「リンク本来の機能」を失います。遷移には <a href>、その場の操作には <button> を使うのが確実です(意味論の面でも正しい選択です)。
また、a と button は互いに入れ子にしません(どちらもインタラクティブ要素のため入れ子は不可・非推奨)。
ページやURLへ「移動」するだけなら <a href> を使うのが基本です。button はページ内の開閉や送信など「その場の操作」を担います。
HTML
<!-- こう書けばOK:遷移は a -->
<a href="/next">次へ</a>
<!-- どうしても button で遷移したいとき -->
<button type="button" onclick="location.href='/next'">次へ</button>
submit)type="button" を明示onkeydown など)※ 入力欄が1つだけの簡単なフォームでは、送信ボタンが見えていなくても Enter で送信されることがあります(暗黙送信)。送信させたくない場面では、クリック専用ボタンに type="button" を付ける/必要なら入力欄側で Enter を抑止します。
補足:Enter キーでの「暗黙送信」は、フォームの構造(入力欄の数や submit ボタンの有無)に左右されます。期待に頼らず、クリック専用ボタンには type="button" を付け、必要なら入力欄で Enter を抑止しましょう。
HTML
<!-- HTML(例):クリック専用ボタンは type="button" を明示 -->
<button type="button" onclick="openPanel()">開く</button>
JavaScript
// JS:対象フォームに限定し、テキスト入力のEnterだけ送信を抑止
const form = document.getElementById('myForm'); // ← 実フォームのIDに置換
if (form) {
form.addEventListener('keydown', function (e) {
const t = e.target;
const isTextArea = t && t.tagName === 'TEXTAREA';
const isContentEditable = t && t.nodeType === 1 && t.isContentEditable === true;
const isTextInput =
t && t.tagName === 'INPUT' &&
!['button', 'submit', 'reset', 'checkbox', 'radio', 'file', 'range', 'color', 'image', 'hidden'].includes((t.type || '').toLowerCase());
// IME入力中(かな漢字変換の確定)は isComposing=true なので止めない
if (!isTextArea && !isContentEditable && isTextInput && e.key === 'Enter' && !e.isComposing) {
e.preventDefault(); // テキスト入力欄でのEnter送信だけ抑止
}
});
}
ポイント:ボタンやリンクにフォーカスがある状態の Enter は既定で“押す”動作になります。上のように対象を絞れば、それらの操作は壊さず、「入力中の誤送信だけ」を防げます。
補足:実務では document 全体ではなく、対象フォーム(例:const form = document.getElementById('myForm'))に絞って form.addEventListener('keydown', ...) とするほうが副作用が少なく安全です。
フォーム内のボタンは既定で type="submit" です。テキスト入力中に Enter を押すと「暗黙の送信」が起きます。クリック用途だけのボタンは type="button" を明示します。
HTML
<button type="button">クリックだけ(送信しない)</button>
<button> の中に入れられるのは テキストやアイコンなどの「フレージングコンテンツ」 です。
他のボタン・リンク・入力など「別のインタラクティブ要素」を入れるのはNG。思わぬ挙動やアクセシビリティ問題の原因になります。
例:OK(テキスト、<img>、<svg>、<span> など)/ NG(<a>、別の <button>、<input> など「インタラクティブ要素」)。
理由:ボタンの中に別のボタンやリンクを入れると、フォーカスの移動順やクリックの判定が壊れ、スクリーンリーダーなどでは「どれが押せる要素か」が正しく伝わらなくなります。HTML仕様でも「インタラクティブ要素の入れ子は禁止」と定義されており、支援技術やブラウザの動作が不安定になります。
アイコンのみのボタンは 視覚的なラベルがない ので、aria-label="閉じる" のように 意味が伝わるラベルを明示 しましょう。
補足 : title 属性でも補助的に説明できますが、スクリーンリーダーには aria-label を付けるのがより確実です。
遷移に button を使う場合は、最短で下のように書けます(本来は a 推奨)。
注意:この方法は「今のタブで移動」だけです。中クリックや長押しメニュー、URLコピーなどの“リンクらしい操作”はできません。URL遷移は基本的に <a href> を使いましょう。
HTML
<button type="button" onclick="location.href='/next'">次へ</button>
formaction で送信先をボタンごとに切り替え同じフォームでも、ボタンごとに送信先・メソッド等を変えられます(確認画面/本送信など)。
HTML
<form id="contact" action="/send" method="post">
<input name="email" required>
<button type="submit">通常送信</button>
<button type="submit" formaction="/confirm" formmethod="get" formtarget="_blank">確認画面へ</button>
</form>
form 属性)ボタンがフォームの外にあっても、form 属性で対象フォームを指定できます。
HTML
<form id="f1" action="/send" method="post">... </form>
<button type="submit" form="f1">このフォームを送信</button>
CSS
button {
display: inline-block;
padding: 1ex 2ex;
margin: 1ex;
font-size: larger;
box-shadow: 0.2ex 0.2ex 0.3ex;
}
おすすめ:キーボード操作に優しいよう、:focus-visible のスタイル(アウトライン)を消さずに 見やすく しましょう。button:disabled { cursor: not-allowed; opacity: .6; } のような無効時の見た目を付けるのも有効です。
CSS
button:focus-visible {
outline: 2px solid blue; /* キーボード操作時に見やすい枠 */
outline-offset: 2px;
}
このようにしておくと、キーボード操作でもボタンの位置がはっきりわかります。
※ :focus のアウトラインは消さないのが基本です。どうしても見た目を変えたい場合は、このページのように :focus-visible で「キーボード操作のときだけ見やすく」するのがおすすめです。
onclick 属性でクリック時の動作を指定できます。
実務では、保守性や再利用性のためにインラインの onclick ではなく、外部JSで addEventListener('click', …) を使う方法が推奨されます。
実務のコツ:フォームの中で使うときは、誤送信防止のため type="button" を付けたうえで onclick を使います。
HTML + JavaScript
<button type="button" onclick="myFunction()">クリック</button>
<script>
function myFunction() {
alert("ボタンがクリックされました!");
}
</script>
popovertarget と popovertargetaction は、popover 属性を持つ要素(小さなパネルやメニュー等)を <button> から開閉するための HTML標準の仕組みです。JavaScriptを書かずに「開く/閉じる/トグル」を実現できます。
popovertargetid を指定します(例:menu)。popovertargetactiontoggle(既定)/show/hide のいずれか。popover(対象側の属性)auto で、外側クリックや Esc などで閉じられます。popover="manual" とすると自動では閉じず、JSや popovertargetaction で制御します。
使いどころ:メニュー/簡易ダイアログ/補助パネル/「詳細」パネルなどの軽量UIに適します。ページ遷移は <a href> を使うのが基本です(このページの方針と同じ)。
HTML
<!-- 開閉ボタン:デフォルトは toggle -->
<button type="button" popovertarget="menu">メニュー</button>
<!-- 対象:小さなパネル -->
<div id="menu" popover>
<p>メニュー内容…</p>
<button type="button" popovertarget="menu" popovertargetaction="hide">閉じる</button>
</div>
ポイント:toggle は「開いていれば閉じる/閉じていれば開く」。確実に開きたい・閉じたい場合は show / hide を指定します。
HTML
<button type="button" popovertarget="tips" popovertargetaction="show">ヘルプを開く</button>
<button type="button" popovertarget="tips" popovertargetaction="hide">ヘルプを閉じる</button>
<div id="tips" popover>
<p>ここに補助説明。外側クリックでも閉じられます。</p>
</div>
ここに補助説明。外側クリックでも閉じられます。
モーダル風に「明示操作でしか閉じてほしくない」ケースでは、popover="manual" を使います。
HTML + JavaScript
<button type="button" id="openDialog">詳細を開く</button>
<div id="dialog" popover="manual">
<h4>詳細設定</h4>
<button type="button" id="closeDialog">閉じる</button>
</div>
<script>
// Progressive Enhancement(対応環境のみ実行)
const dlg = document.getElementById('dialog');
const openBtn = document.getElementById('openDialog');
const closeBtn = document.getElementById('closeDialog');
const canPopover = typeof HTMLElement !== 'undefined'
&& 'showPopover' in HTMLElement.prototype;
if (canPopover && dlg && openBtn && closeBtn) {
openBtn.addEventListener('click', () => dlg.showPopover());
closeBtn.addEventListener('click', () => dlg.hidePopover());
}
</script>
詳細設定
popover="manual" は外側クリックや Esc では閉じません。hidePopover() または popovertargetaction="hide" を用意します。canPopover)で未対応環境を回避できます。canPopover 分岐で「何もしない」ようにしています。必要なら同じUIを position:absolute + JS で簡易再現するなどのフォールバックを用意しましょう。[popover] は通常の要素なので、背景・影・位置などを自由に装飾できます(必要なら絶対配置)。
CSS
[popover] {
padding: 1em;
box-shadow: 0 2px 8px rgba(0,0,0,.2);
border-radius: .5em;
max-inline-size: 28rem;
}
<a href> / 既定送信を優先。toggle / show / hide を用途で使い分ける。<button> の中に別のインタラクティブ要素を入れ子にしない(ボタン内入れ子はNG)。popover を付け忘れて反応しないpopover が必要です。id の誤りで開かないpopovertarget は id と一致させます(# は不要)。popover="manual" は自動で閉じません。既定(auto)は外側クリック等で閉じます。連打で重複送信しないよう、クリック直後にボタンを一時的に無効化し、送信自体は既定動作に任せます(サーバー側でもトークン等で対策すると万全)。
HTML
<!-- こうすればOK:無効化だけ行い、送信は既定動作に任せる -->
<button type="submit" onclick="this.disabled=true;">送信</button>
ポイント:この方法なら required などのフォーム検証や submit イベントがそのまま働きます。JavaScriptで form.submit() を呼ぶと検証やイベントを飛ばすことがあるため、既定の送信に任せるのが安全です。
送信後に同ページで完結する設計(非同期送信・SPAなど)の場合は、サーバー応答後にボタンを再び有効化しましょう。
HTML + JavaScript
<form id="contact" action="/submit" method="post">
<input name="email" required>
<button id="sendBtn" type="submit" onclick="this.disabled=true;">送信</button>
</form>
<script>
const form = document.getElementById('contact');
const btn = document.getElementById('sendBtn');
// 既定の送信を止めずに、送信後の再有効化だけ責務を持つ想定
form.addEventListener('submit', function () {
// 非同期送信などでページ遷移しないケースを想定
fakeApi().then(() => {
// 正常終了後に再び押せるようにする
if (btn) btn.disabled = false;
}).catch(() => {
// エラー時もユーザーが再試行できるようにする
if (btn) btn.disabled = false;
});
});
function fakeApi() {
return new Promise(resolve => setTimeout(resolve, 1000));
}
</script>
補足:ページ遷移が発生する(通常の同期送信)なら、再有効化は不要です。フォーム検証でエラーがある場合は送信されず、そのままブラウザの検証UI(メッセージ表示やエラー欄へのフォーカス移動)が働きます。
formactionformmethodformtarget_blank)formenctypemultipart/form-data)formnovalidateautofocus豆知識:formaction / formmethod / formtarget / formenctype / formnovalidate は、基本的に type="submit" のボタンに意味があります。
特に formenctype は POST のときだけ有効です。(JS で送信を統一する場合は、後述の form.requestSubmit() を使うと「どのボタンが押されたか」に応じた送信ができます。)
submit。対処:クリック用途は type="button" を明示。button を使っている。対処:遷移は <a href> を基本に。formaction(必要なら formmethod / formtarget)。disabled が付いている/重なりでクリックを奪われている。対処:属性確認とCSSの重なり(z-index, pointer-events)確認。aria-label か視覚的に隠したテキストを追加。this.disabled=true を入れる/サーバー側でも重複防止トークン。form.submit() を呼んでいる。対処:form.requestSubmit() を使う。aria-disabled だけでJS抑止がない。対処:disabled を使う/click を preventDefault()。button + JS で実装。対処:遷移は <a href> に。見た目は同じでも、人によって操作のしかた・見え方は違います。HTMLで意味を作り、必要なところだけARIAで補助情報を足すと、読み上げやキーボード操作でも使いやすくなります。
見た目がアイコンだけでも、音声読み上げに名前を伝えます。
HTML
<button type="button" aria-label="閉じる">✕</button>
aria-pressed)トグル(ON/OFF)ボタンのように状態を表すときは、aria-pressed を使います。
HTML
<button type="button" aria-pressed="false" id="muteBtn">ミュート</button>
<script>
const b = document.getElementById("muteBtn");
b.addEventListener("click", () => {
const on = b.getAttribute("aria-pressed") === "true";
b.setAttribute("aria-pressed", String(!on));
});
</script>
disabled と aria-disabled の使い分けdisabled はフォーカス不可・クリック不可・送信対象外です。見た目だけ無効風にしたい(条件で有効化する等)なら aria-disabled="true" を使い、JavaScriptで実際の動作を制御します。
HTML
<!-- 完全に無効 -->
<button type="button" disabled>処理中...</button>
<!-- 見た目だけ無効(キー操作は無効化するJSが別途必要) -->
<button type="button" aria-disabled="true">準備中</button>
注意:aria-disabled="true" は見た目や読み上げでは「無効」に聞こえますが、実際にはクリックできます。押せないようにするには disabled を使うか、click イベントで event.preventDefault() などの制御が必要です。キーボード操作(Enter/Space)も同様に抑止してください。
<button> は既定で Enter/Space で“押せます”。独自JSでこの挙動を壊さないこと。:focus-visible で見やすく(消さない)。詳しくは「CSS で見た目を変更」を参照。type="button" を付け、送信ボタンは1つに絞ると安全です。必要なら keydown で Enter を抑止します。<a href>が基本です。<button>+JS だとリンクの便利機能が失われやすいです。type="submit" のボタンに formaction(必要に応じて formmethod/formtarget)を付けます。aria-label で意味を明示しましょう(例:閉じる)。disabled 一択。aria-disabled は見た目/読み上げ用なので、JSでクリック抑止が必要です。submit イベントも自然に動きます)。JSで送信を統一したい場合のみ、type="button" + form.requestSubmit() を使います。「押されたボタン」の設定を効かせつつ送信
JavaScript
<form id="contact" action="/send" method="post">...</form>
<button type="submit" id="sendA" formaction="/a">Aへ送信</button>
<button type="submit" id="sendB" formaction="/b">Bへ送信</button>
<script>
// どのボタン相当で送信するかを明示できる:requestSubmit(button)
const form = document.getElementById('contact');
document.getElementById('sendA').addEventListener('click', (e) => {
e.preventDefault(); // 既定送信を止める
form.requestSubmit(e.currentTarget); // sendAの設定(formactionなど)で送信
});
document.getElementById('sendB').addEventListener('click', (e) => {
e.preventDefault();
form.requestSubmit(e.currentTarget); // sendBの設定で送信
});
</script>
form.submit() は検証や submit イベントを飛ばすのに対し、requestSubmit(button) はブラウザの通常フロー(検証・イベント)を通し、かつ “そのボタンの属性” を効かせられるので安全です。