Bubble API & Events
The <saytv-chat-bubble> element exposes a JavaScript API for programmatic control and dispatches custom events for integration with your application.
JavaScript API
Access the bubble element and call methods directly:
const bubble = document.querySelector('saytv-chat-bubble');Methods
| Method | Description |
|---|---|
open() | Open the chat panel. Creates the inner widget on first call. |
close() | Close the chat panel with a closing animation. |
toggle() | Toggle the panel open/closed. |
show() | Show the bubble (after it was hidden). |
hide() | Hide the entire bubble (launcher + panel). Closes the panel first if open. |
destroy() | Full teardown - removes the inner widget, clears timers, and removes event listeners. |
open()
Opens the chat panel. If this is the first time opening, the inner <saytv-chat> widget is created and mounted. Subsequent calls show the existing widget without re-creating it.
bubble.open();The greeting tooltip is automatically dismissed when the panel opens. The unread badge is reset to zero.
close()
Closes the chat panel with a slide-down animation (200ms). Focus returns to the launcher button.
bubble.close();toggle()
Convenience method that opens the panel if closed, or closes it if open.
bubble.toggle();show() / hide()
Control the visibility of the entire bubble element. hide() closes the panel first if it's open.
// Hide the bubble when user navigates to a page where chat isn't needed
bubble.hide();
// Show it again
bubble.show();Internally, hide() sets the hidden attribute and show() removes it.
destroy()
Tears down the bubble completely. Removes the inner widget, clears greeting and animation timers, and removes all event listeners (keyboard, outside click, mobile detection).
bubble.destroy();WARNING
After calling destroy(), the bubble element remains in the DOM but is inert. Remove it from the DOM if you no longer need it:
bubble.destroy();
bubble.remove();Events
The bubble dispatches its own events prefixed with saytv:. These are standard CustomEvent objects that bubble up through the DOM.
| Event | Fired When | Detail |
|---|---|---|
saytv:open | Chat panel opens | - |
saytv:close | Chat panel closes | - |
saytv:ready | Inner <saytv-chat> widget fires stv:ready | - |
saytv:unread | Unread message count changes | { count: number } |
Listening to Events
const bubble = document.querySelector('saytv-chat-bubble');
bubble.addEventListener('saytv:open', () => {
console.log('Chat opened');
});
bubble.addEventListener('saytv:close', () => {
console.log('Chat closed');
});
bubble.addEventListener('saytv:ready', () => {
console.log('Chat widget is ready');
});
bubble.addEventListener('saytv:unread', (e) => {
console.log('Unread messages:', e.detail.count);
});Unread Badge
The bubble tracks incoming messages via the inner widget's stv:message-received event. When the panel is closed, each new message increments the unread count and the saytv:unread event fires. When the panel opens, the count resets to zero.
bubble.addEventListener('saytv:unread', (e) => {
// Update your page title or favicon
document.title = e.detail.count > 0
? `(${e.detail.count}) My Site`
: 'My Site';
});The badge displays counts up to 99 and shows "99+" for higher values.
Inner Widget Events
All standard <saytv-chat> events (stv:ready, stv:message-sent, stv:user-login, etc.) are dispatched on the inner widget element inside the shadow DOM. They do not automatically propagate to the bubble element.
To listen for inner widget events, use the saytv:ready event to know when the widget exists, then access it:
bubble.addEventListener('saytv:ready', () => {
// The inner widget is now available in the shadow DOM
const widget = bubble.shadowRoot.querySelector('saytv-chat');
widget.addEventListener('stv:message-sent', (e) => {
console.log('Message sent:', e.detail.message);
});
widget.addEventListener('stv:user-login', (e) => {
console.log('User logged in:', e.detail.user);
});
});Common Patterns
Open bubble from a custom button
Use the JavaScript API to trigger the bubble from anywhere in your page:
<button id="help-btn">Need Help?</button>
<saytv-chat-bubble app-id="your-app-id"></saytv-chat-bubble>
<script>
document.getElementById('help-btn').addEventListener('click', () => {
document.querySelector('saytv-chat-bubble').open();
});
</script>Hide on specific pages
const bubble = document.querySelector('saytv-chat-bubble');
// Hide on checkout pages
if (location.pathname.startsWith('/checkout')) {
bubble.hide();
}Analytics integration
bubble.addEventListener('saytv:open', () => {
analytics.track('chat_bubble_opened');
});
bubble.addEventListener('saytv:close', () => {
analytics.track('chat_bubble_closed');
});
bubble.addEventListener('saytv:unread', (e) => {
if (e.detail.count > 0) {
analytics.track('chat_unread_message', { count: e.detail.count });
}
});Conditionally show based on user state
const bubble = document.querySelector('saytv-chat-bubble');
// Only show for logged-in users
if (!currentUser) {
bubble.hide();
}
// Show after a delay for first-time visitors
if (isFirstVisit) {
setTimeout(() => bubble.show(), 10000);
}