Reacting to what the widget does
The widget tells your page when something happens inside it. You listen for a normal DOM event and do whatever your site needs — close a panel, route with your own framework, record an analytics hit.
You don’t need any of this. The widget works untouched. These are for the cases where the widget can’t know what your page wants.
How to listen
Every widget event is bubbling and composed, which means it crosses the
Shadow DOM boundary and reaches your page. Listen on the element, or on window if the element isn’t in the DOM yet:
<ask-pod-widget id="ask" feed-id="..." api-key="pk_live_..."></ask-pod-widget>
<script>
document.getElementById('ask').addEventListener('podsaid:navigate', (e) => {
console.log(e.detail);
});
</script> Events are namespaced with podsaid: so they can’t collide with your own.
podsaid:navigate
Fires when a visitor clicks something in the widget that leads to a page —
a citation card, the source link on a card, or an inline [1] chip in an
answer. It fires before the widget navigates.
e.detail = {
feedId: 'mdumc-web',
href: 'https://example.org/worship#:~:text=Communion',
target: 'same' | 'cross',
source: 'card' | 'card-link' | 'answer-chip',
citationIndex: 1
} target is the field most recipes branch on.
same— the href is on your origin. The widget clicks a real link so your router can intercept it. This tab is about to move.cross— the href is somewhere else. The widget opens a new tab, because replacing this page would take the visitor’s conversation with it. This tab stays where it is.
Audio and video citations never fire this. They play inside their card, so nothing navigates.
Recipe: close your panel
If you mount the widget in a slide-out drawer or a modal, a same-origin citation leaves it sitting on top of the page the visitor just asked to see — your router keeps the page alive, and the panel with it. That’s usually what you want for a conversation; it isn’t what you want here.
widget.addEventListener('podsaid:navigate', (e) => {
if (e.detail.target === 'same') closeDrawer();
}); Check target. On cross the visitor got a new tab and never left your page,
so closing the panel would throw away a conversation they’re still in.
Recipe: use your own router
The widget navigates by creating a link and clicking it, because that is what a document-level router listens for. If your framework wants an explicit call instead, cancel the event and make it:
widget.addEventListener('podsaid:navigate', (e) => {
if (e.detail.target !== 'same') return; // let new tabs happen normally
e.preventDefault(); // we'll handle it
router.push(e.detail.href); // Next.js, React Router, etc.
}); preventDefault() stops the widget navigating. Nothing else changes — if you
cancel and then don’t navigate, the visitor stays put.
Recipe: send it to analytics
widget.addEventListener('podsaid:navigate', (e) => {
gtag('event', 'citation_click', {
feed_id: e.detail.feedId,
destination: e.detail.href,
position: e.detail.citationIndex
});
}); podsaid:chip_click
Fires when a visitor clicks a suggested follow-up question. The widget submits it as a new question either way; this is for measuring how often the suggestions get used.
e.detail = {
feedId: 'mdumc-web',
suggestedRewrite: 'What time are Sunday services?',
variantResolved: 'no_results'
} Not cancelable.
Notes
These events are additive and stay that way. New fields may appear in detail; existing ones keep their meaning. Read the fields you need and
ignore the rest.
A listener that throws won’t break the widget. Dispatch is wrapped, so a bug in your handler can’t strand a click. It can still stop your own recipe from running, so check your console.
Every event also logs to the console with a [podsaid-widget] prefix,
which is usually the fastest way to confirm a listener is attached to the
right element.