How to Keep Your Radio or Audio Player Playing While Visitors Browse Your WordPress Site
EDITOR NOTE — remove before publishing. Two animations belong in this post. The source files are in the plugin repo at assets/animations/: player-continuity-hero.svg and player-setup-walkthrough.svg. Upload both to the Media Library (SVG uploads may need enabling) and swap them into the two placeholders marked below. Status notes on the player list are accurate as of 2.4.2 — update after the compatibility pass.
If you run a radio station, a podcast, or any site where people listen while they read, you know the problem. A visitor starts the stream, clicks through to another page, and the audio cuts out. They have to press play again. Most don’t.
AjaxPress fixes this by keeping your player alive across navigation. This guide walks through the actual setup — where the setting lives, what to type into it, and the one styling change that trips up almost everyone.
Before you start: where is your player?
Two things decide which route you take.
1. Your player must render on every page. If it sits inside the content of one page, it cannot persist — the content area is what gets replaced on navigation. Move it into your footer, header, or a template that outputs site-wide. Most radio plugins have a “sticky” or “floating” mode that does this for you. Turn it on.
2. Can you edit the player’s HTML?
- Yes — you added it with a code snippet, a Custom HTML block, or a theme file → Route A, free, one attribute.
- No — a theme or plugin renders it (Sonaar, Mediastream, Elementor’s audio widget, a radio plugin) → Route B, needs Pro.
Route A: you can edit the markup (free)
Add data-ajaxpress-persist to the element that wraps your whole player — not the <audio> tag inside it. The play button, volume slider and track title all need to travel with it.
<div id="radio-bar"
data-ajaxpress-persist
data-persist-key="live-radio">
<audio id="radio-audio" src="https://stream.example.com/live" preload="none"></audio>
<button id="radio-play">Play</button>
<span id="radio-title">Live</span>
</div>
Three other markers work identically, if one suits your setup better: the class ajaxpress-persist, an id starting with persistent-, or data-persist directly on an <audio> or <video> tag.
Now skip to Give your player a stable identity — it applies to both routes.
Route B: a plugin or theme renders it (Pro)
You can’t add an attribute to markup you don’t control, so instead you tell AjaxPress where the player is.
Step 1 — Find your player’s selector
Open your site, right-click the player and choose Inspect. In the panel that opens, walk up the HTML until you find the outermost element containing every part of the player — audio, buttons, artwork, title.
Note its id (write it as #player-wrap) or, failing that, a class that appears only once on the page (.my-radio). If you’re unsure, right-click the element in the inspector and choose Copy → Copy selector.
Check it before you go further. Open the browser console and run:
document.querySelectorAll('#your-selector').length
You want exactly 1. Zero means the selector is wrong. More than one means it’s too broad and will match things you don’t want moved.
Step 2 — Open Settings → AjaxPress → Advanced
In your WordPress dashboard, go to Settings → AjaxPress, then click the Advanced tab in the left sidebar.
Step 3 — Paste the selector into Media Players
Find the Media Players field and enter your selector:
#radio-bar
Several players? Separate them with commas:
#radio-bar, .sonaar-sticky-player, #mediastream-player
Step 4 — Save Settings
Click Save Settings, then go to your site, start the player and click through to another page. The audio should carry straight over.
Give your player a stable identity
AjaxPress has to recognise your player as the same player on each new page. It looks for, in order: a data-persist-key attribute, then the element’s id, then the src of the audio or iframe inside it, and finally a fingerprint of the HTML.
That last fallback is fragile, and it causes the most confusing bug of the lot. If your stream URL carries a cache-busting parameter like ?t=1736301234, the URL differs on every page. AjaxPress sees a brand-new player each time, keeps the old one and adds the new one — and you get two streams playing at once.
If you’ve ever heard doubled audio, this is why. Give the wrapper a fixed key:
<div id="radio-bar" data-persist-key="live-radio">
The part that catches everyone: your player moves
To survive navigation, your player is physically moved out of the page and into a container AjaxPress adds at the end of <body>:
<div id="ajaxpress-persist" style="position: fixed; z-index: 1000000;">
<div data-ajaxpress-lifted data-persist-key="live-radio">
<!-- your player, live, listeners intact -->
</div>
</div>
This has two consequences worth knowing before they surprise you.
Your player leaves its old parent. If it lived in your footer, it isn’t in your footer any more. CSS written as .site-footer .radio-player { … } stops applying.
It has to position itself. The container is position: fixed with no offsets, so the player lands wherever the browser considers its natural spot — usually the very bottom of the document.
Sound familiar? “I added the selector and now the widget just moves up the page.” That’s this. Nothing is broken — the player moved, and it has no positioning of its own.
The fix is to give it self-contained positioning:
#radio-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 10;
}
And rewrite any parent-scoped styles so they stand alone:
/* Before — stops working once the player is lifted */
.site-footer .radio-player { background: #111; padding: 12px; }
/* After */
.radio-player { background: #111; padding: 12px; }
Selectors for common players
Starting points — always confirm against your own site, since markup varies by version.
| Player | Try this | Status |
|---|---|---|
| Custom HTML5 + stream URL | data-ajaxpress-persist on the wrapper | Confirmed |
| Mediastream | #mediastream-player | Works, with caveats |
| MP3 Audio Player by Sonaar (sticky) | .sonaar-sticky-player | Works, with caveats |
| Elementor Audio / Video widget | Set a CSS ID under Advanced, then use it | Unverified |
| Radio Player | .radio-player-container | Unverified |
| StreamCast | .streamcast-player | Unverified |
| WaveSurfer | #waveform | Unverified |
“Unverified” means the selector comes from the plugin’s markup but we haven’t tested it on a live install. We’d rather tell you that than have you trust something nobody has run. If you get one working, let us know and we’ll update this page.
Known issues, honestly
Current as of version 2.4.2. These are real and reported — listed so you don’t lose an afternoon to one.
Player carried over but invisible or unclickable. Inline visibility: hidden; opacity: 0; pointer-events: none can end up on the moved player. Workaround:
#ajaxpress-persist [data-ajaxpress-lifted] {
visibility: visible !important;
opacity: 1 !important;
pointer-events: auto !important;
}
Excluded pages don’t fire ajaxpress:ready. If you exclude a page under Advanced → Exclude Links, that event no longer fires there, so players that initialise on it won’t start. No clean workaround yet — avoid excluding pages that contain a player.
Player only appears after a second click. Reported with Mediastream on some setups. A data-persist-key has resolved it in some cases.
Checklist
- The player renders on every page, not just one
- The outermost wrapper is marked, not just the
<audio>tag - It has a
data-persist-key, or anididentical on every page - It positions itself and doesn’t rely on its old parent
- Your selector matches exactly one element
- Tested across 5+ pages, including the Back button
- Tested landing on a page without a player, then navigating to one that has it
Still stuck?
Email [email protected] with the plugin or theme rendering your player, a URL where we can see it, and what you’ve already tried. A live URL is worth more than any description — it’s usually the difference between a same-day answer and a week of back-and-forth.