Keep Audio or Video Playing Across Pages
Normally, clicking a link in WordPress reloads the page and your audio stops. AjaxPress keeps the player alive so a stream, podcast or video never cuts out while visitors browse.
Prerequisite: the player must render on every page
If the player sits inside the content of a single page, it cannot persist — the content area is exactly what gets replaced during navigation. Put it in a footer, header, or a template that outputs site-wide. Most radio plugins offer a sticky or floating mode that does this.
Two routes
| Situation | Route | Licence |
|---|---|---|
| You can edit the player’s HTML | Add a marker attribute | Free |
| A theme or plugin renders it | Media Players CSS selector | Pro |
Route A — marker attributes (free)
Add any one of these to the element that wraps your player. All four behave identically.
<div id="radio-bar" data-ajaxpress-persist>…</div>
<div id="radio-bar" class="ajaxpress-persist">…</div>
<div id="persistent-radio">…</div>
<audio data-persist src="https://stream.example.com/live"></audio>
Mark the outermost wrapper, not the <audio> tag inside it. Play buttons, volume sliders and artwork have to travel with the player or they stop working.
Route B — Media Players selector (Pro)
Go to Settings → AjaxPress → Advanced → Media Players and enter a CSS selector:
#mediastream-player, .sonaar-sticky-player
Any CSS selector works. Verify it matches exactly one element before saving:
document.querySelectorAll('#your-selector').length // want: 1
Give the player a stable identity
AjaxPress must recognise your player as the same player on every page. It resolves identity in this order:
| # | Source | Reliability |
|---|---|---|
| 1 | data-persist-key | Best — you control it |
| 2 | The element’s id | Good, if identical on every page |
| 3 | src of an inner iframe/audio/video | Breaks if the URL varies |
| 4 | The element’s own src | Same caveat |
| 5 | Fingerprint of the opening HTML | Last resort, fragile |
<div data-ajaxpress-persist data-persist-key="live-radio">…</div>
Why it matters. If your stream URL carries a cache-busting parameter such as ?t=1736301234, that URL differs on every page. AjaxPress sees a new player each time, keeps the old one and adds the new one — resulting in two streams playing at once. A fixed key prevents this.
The player is moved — plan for it
To survive navigation, the player is physically moved out of the page into a container appended to <body>:
<div id="ajaxpress-persist" style="position: fixed; z-index: 1000000;">
<div data-ajaxpress-lifted data-persist-key="live-radio">
<!-- your player, live element, listeners intact -->
</div>
</div>
- It leaves its old parent. CSS written as
.site-footer .radio-player { … }stops applying. - It must position itself. The container is
position: fixedwith no offsets, so the player lands at its natural position — usually the bottom of the document.
Symptom: “I added the selector and the widget just moves up the page.” That is this behaviour. Fix it with self-contained positioning:
#radio-bar {
position: fixed;
bottom: 0; left: 0; right: 0;
z-index: 10;
}
How it works internally
- On each navigation, AjaxPress matches the built-in markers plus anything in Media Players.
- The first match is moved — not copied or rebuilt — into
#ajaxpress-persist. Because the live element moves, its event listeners, playback position and any embedded player iframe survive. - On later pages, that page’s own copy is removed; the one already playing is the real one.
- If a page has no player, the playing one is removed. Continuity follows your content.
Rebuilding from HTML would drop every event listener, so a JavaScript-driven widget would render correctly and do nothing on click. Moving the live element avoids that.
Never carried into wp-admin. Moving between the front end and the dashboard is always a full page load.
Known issues (2.4.2)
Lifted player is invisible or unclickable. Inline visibility: hidden; opacity: 0; pointer-events: none can end up on it. Workaround:
#ajaxpress-persist [data-ajaxpress-lifted] {
visibility: visible !important;
opacity: 1 !important;
pointer-events: auto !important;
}
ajaxpress:ready does not fire on excluded pages. Players that initialise on that event will not start on pages added to Exclude Links. No clean workaround yet.
Player appears only after a second click. Reported with Mediastream. A data-persist-key resolves it in some cases.
Checklist
- Player renders on every page, not just one
- The outermost wrapper is marked, not the
<audio>tag data-persist-keyset, or anididentical on every page- Player positions itself, independent of its old parent
- Selector matches exactly one element
- Tested across 5+ pages including Back, and a page with no player