jQuery / Method / .fadeToggle() - Reverse Each Element’s Visibility
jQuery's .fadeToggle() gradually hides visible elements and reveals hidden ones. Selecting multiple elements reverses each element's own state rather than making the entire set uniformly visible or hidden. Use .fadeIn() to ensure visibility or .fadeOut() to ensure hiding.
Per-element toggling and arguments
The .fadeToggle() documentation gives .fadeToggle(duration, easing, complete) with optional arguments. Duration is the running time: default 400 ms, "fast" 200 and "slow" 600. Higher numbers mean slower changes, not a wait before starting.
Default easing is swing. Specify linear after duration for constant progress, as in .fadeToggle(800, "linear"), rather than placing linear alone in the duration slot. complete runs after that element's effect finishes in either direction.
| Method | Purpose | Result from one visible, two hidden |
|---|---|---|
| .fadeToggle() | Reverse each state | one hidden, two visible |
| .fadeIn() | Show direction | Both visible |
| .fadeOut() | Hide direction | Both hidden |
Unlike .fadeIn() and .fadeOut(), completing two toggles returns to the original state. Hiding ends in display: none and removes layout space without deleting the DOM element.
Complete example with opposite initial states
Save the entire code as UTF-8 in fadetoggle-demo.html and open it in a browser. No installation is needed, but loading the official full jQuery file from its Content Delivery Network (CDN) requires internet. The slim file excludes effects and cannot run this example.
The code uses 4.0.0. State reversal, linear progress, completion counts, repeated-input handling and replay after reset were checked with 3.7.1 and 4.0.0. one begins visible and CSS hides two. Controls stay outside both panels.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Toggle each tile's visibility</title>
<style>
body { font: 16px/1.6 sans-serif; margin: 24px; }
.tile { padding: 20px; margin: 12px 0; background: #e4f2ef; }
#two { display: none; }
button, select { padding: 8px; margin: 4px; }
</style>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
</head>
<body>
<h1>Toggle each tile's visibility</h1>
<label for="duration">Duration:</label>
<select id="duration"><option value="800">800 ms</option><option value="200">200 ms</option></select>
<label><input type="checkbox" id="instant"> Toggle instantly</label>
<button type="button" id="toggle" aria-controls="one two">Toggle both</button>
<button type="button" id="reset">Reset</button>
<div id="tiles">
<div class="tile" id="one">Tile one starts visible</div>
<div class="tile" id="two">Tile two starts hidden</div>
</div>
<p id="result" aria-live="polite"></p>
<script>
$(function () {
const $tiles = $("#tiles > .tile");
let busy = false;
let completed = 0;
function report() {
const ids = $tiles.filter(":visible").map((_, tile) => tile.id).get().join(", ");
$("#result").text(`visible=${ids || "none"} | completed=${completed}`);
}
$("#toggle").on("click", function () {
if (busy || !$tiles.length) return;
busy = true;
completed = 0;
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const duration = $("#instant").prop("checked") || reduce
? 0 : Number($("#duration").val());
$("#result").text("animating | completed=0");
$tiles.fadeToggle(duration, "linear", function () {
completed++;
if (completed === $tiles.length) {
busy = false;
report();
}
});
});
$("#reset").on("click", function () {
$tiles.stop(true, true).hide().css("opacity", "");
$("#one").show();
busy = false;
completed = 0;
report();
});
report();
});
</script>
</body>
</html>
Initially only one is visible:
visible=one | completed=0
- Leave Toggle instantly off, select 800 ms and press Toggle both. one fades out while two fades in; animating appears during the transition.
- After both finish, the result is
visible=two | completed=2. completed counts finished elements, not clicks. - Complete another toggle to return to
visible=one | completed=2. The count resets to zero at each accepted activation. - Select 200 ms for faster reversal. Additional clicks during an effect are ignored by this example's busy check.
- Press Reset while running to restore one visible, two hidden and completed=0. Verify that the next Toggle both reveals two.
- Toggle instantly or a reduced-motion preference uses duration zero. The native button also supports Enter after receiving focus.
Completion counts and repeated-input policy
.fadeToggle() does not block later JavaScript until completion. Its callback runs once per selected element, so the example reports visible IDs only when completed reaches $tiles.length. Finishing a hide also counts as successful completion.
busy is a flag created by this exercise, not a jQuery argument. It becomes true on start and false after both finish. This design ignores extra input instead of reversing immediately mid-effect. Distinguish it from accumulating every request in an animation queue.
If an Open button must always open, choose an explicit show operation rather than toggle. Likewise, use .fadeIn() or .fadeOut() to bring panels with different initial visibility to one common state.
Reset order and troubleshooting
In Reset's .stop(true, true), the first true clears the queue and the second finishes the current effect immediately. Completion callbacks can run then. Only afterward does the example hide both, show one, reset busy and completed, and report the final state. Keep this order so old completion cannot overwrite restored values.
Toggle effects track in-progress state. Merely stopping halfway and setting CSS to resemble the initial state is insufficient verification; also check the next run. This example finishes the current effect, explicitly restores known visibility and verifies both reset and the following reversal.
For reversed expectations, inspect initial CSS and current display. Missing motion calls for checking instant mode and reduced motion; method errors call for inspecting full jQuery loading and Console. A missing set has no per-element completion, so the example checks .length before starting.




