Sometimes a WordPress page needs to toggle between two elements: monthly and yearly prices, a short and a long version of a text, metric and imperial units. One element is visible, a click shows the other one instead.
The older version of this post toggled a CSS class with jQuery on a click anywhere in a <div>, and left the CSS that hides the inactive element to you. This version is complete: a real button, two panels, the HTML hidden attribute to hide one of them, and a few lines of plain JavaScript. It works without any CSS and without jQuery.
The snippet
<?php
/**
* Toggle between two elements: a button switches which of the two panels is shown.
* Scripts Organizer: Script location "Shortcode", output via [scorg_shortcode id="…"].
* Edit the labels and the two panels below.
*/
$dpc_toggle = array(
'show_second' => __( 'Show yearly prices', 'dpc' ),
'show_first' => __( 'Show monthly prices', 'dpc' ),
'first' => '<p><strong>Monthly:</strong> $19 per month, cancel anytime.</p>',
'second' => '<p><strong>Yearly:</strong> $190 per year, two months free.</p>',
);
// Unique IDs, so the shortcode can be used more than once on a page.
$dpc_toggle_id = wp_unique_id( 'dpc-toggle-' );
?>
<div class="dpc-toggle" data-dpc-toggle>
<button type="button" class="dpc-toggle__button" aria-controls="<?php echo esc_attr( $dpc_toggle_id . '-1 ' . $dpc_toggle_id . '-2' ); ?>" data-dpc-label="<?php echo esc_attr( $dpc_toggle['show_first'] ); ?>"><?php echo esc_html( $dpc_toggle['show_second'] ); ?></button>
<div class="dpc-toggle__panel" id="<?php echo esc_attr( $dpc_toggle_id . '-1' ); ?>" aria-live="polite"><?php echo wp_kses_post( $dpc_toggle['first'] ); ?></div>
<div class="dpc-toggle__panel" id="<?php echo esc_attr( $dpc_toggle_id . '-2' ); ?>" aria-live="polite" hidden><?php echo wp_kses_post( $dpc_toggle['second'] ); ?></div>
</div>
<?php
// The script once per page, in the footer, however many toggles the page has.
if ( ! wp_script_is( 'dpc-toggle', 'enqueued' ) ) {
$dpc_toggle_js = <<<'JS'
document.addEventListener('click', function (event) {
var button = event.target.closest('[data-dpc-toggle] > .dpc-toggle__button');
if (!button) {
return;
}
button.parentElement.querySelectorAll(':scope > .dpc-toggle__panel').forEach(function (panel) {
panel.hidden = !panel.hidden;
});
// Swap the visible label with the one stored in data-dpc-label.
var next = button.getAttribute('data-dpc-label');
button.setAttribute('data-dpc-label', button.textContent);
button.textContent = next;
});
JS;
wp_register_script( 'dpc-toggle', false, array(), null, array( 'in_footer' => true ) );
wp_add_inline_script( 'dpc-toggle', $dpc_toggle_js );
wp_enqueue_script( 'dpc-toggle' );
}
Add it with Scripts Organizer
This snippet uses Scripts Organizer’s own Shortcode script location, so the markup lives in the code block and you edit it there, not in the page.
- In Scripts Organizer go to Code Blocks → Add New and give the block a name.
- Set Trigger location to Conditions and leave Template on All. The Shortcode location is only offered with the Conditions trigger.
- Under Script location tick Shortcode, paste the code into the PHP editor, edit the labels and panels in
$dpc_toggle, and publish. - Scripts Organizer now shows a shortcode such as
[scorg_shortcode id="123"]for this block. Copy it, add a Shortcode block where the toggle should appear, and paste it there. Page builders take it in their shortcode element.
The Shortcode location fits here because the toggle has no settings: its content is the HTML you write in the block. Scripts Organizer includes the file every time the shortcode renders, so the code declares no functions and the script is added only once per page.
How it works
- The button is a real
<button>, so it works with the keyboard and screen readers.aria-controlspoints to both panels, andaria-liveannounces the panel that appears. - The second panel starts with the
hiddenattribute. A click flipshiddenon both panels and swaps the button text with the one stored indata-dpc-label. wp_unique_id()gives each toggle its own IDs, so the same shortcode can be on a page several times, and one delegated click listener handles all of them.- The labels are escaped, and the panels go through
wp_kses_post(), so they accept the same HTML as a post. - The script is registered as a handle without a file and printed once in the footer, only on pages that show a toggle.
Customise it
- Different content: change
first,secondand the two labels in$dpc_toggle. For a second toggle with other content, duplicate the code block. - Style it with the classes
dpc-toggle,dpc-toggle__buttonanddpc-toggle__panel, for example with a Header CSS part in the same block or in your theme. - Need the same toggle in several places with different content? Turn the markup into a normal
add_shortcode()with attributes in an Everywhere PHP block.
Gotchas
- Visitors without JavaScript see only the first panel.
- A theme rule like
display: blockon the panels can overridehidden. Add.dpc-toggle__panel[hidden]{display:none}if the second panel shows from the start. - Conditions narrower than All also decide where the shortcode renders: on other pages it outputs nothing.
In the Scripts Organizer library Import it with one click from Scripts Organizer.