Blocks API
Example of Blocks and Block Client APIs
We're gonna show a simple HTMl page that:
- properly embeds a Bubblehouse block;
- implements Block Client API calls to handle resizing and popups;
- uses recommended permission and style adjustments for an optimal user experience.
Running the example
You can save this example as example.html
and open it in your browser from the file system; it should work.
Of course, you can also host this page on your web server and open it from there.
Reusing the example
You are welcome to reuse any code from this page.
Before you copy and paste it, however, it is important that you understand our approach and assumptions.
A big assumption we're making is that the id
of the IFRAME matches the instance
parameter passed into the block URL, and thus matches the instance
property of incoming Block Client calls. We definitely recommend you to follow this practice, although, strictly speaking, it is optional.
We're dispatching Block Client calls using custom DOM events. We find it a very convenient; it allows to handle some calls globally (like Resize1 and OpenPopup1 in our example), while other calls can have instance-specific handlers (like ClosePopup1 here). This, however, is again just a recommended approach, and you might opt to do something completely different.
Note that handling popups is, strictly speaking, optional; with some degradation in user experience, you can have Bubblehouse run all popups inside its IFRAMEs.
We assume that you're embedding the IFRAME cross-origin. In some cases (e.g. when adding our blocks to a Shopify theme), requests should instead go through the Shopify proxy and are same-origin, and you'd be able to omit sandboxing and permission settings on the IFRAME.
Source code
Note that you need to replace:
YOUR_SITE_SLUG_HERE
with something likeyour-site
orbubblehouse-your-site
,YOUR_USER_TOKEN_HERE
with a customer token if a customer is logged in (and an empty string if not).
See your API console for the correct values of both the slug and the token.
If your API console tells you that you're using a Shopify proxy and don't need customer tokens, you should omit auth
parameter or set it to an empty string, and you need to use a different block URL which you can also see in the API console.
<html>
<head>
<title>BubbleSample</title>
</head>
<body style="display: flex; flex-direction: column; background: white; padding: 0; margin: 0; font: 16px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'">
<header style="display: flex; align-items: center; justify-content: center; gap: 20px; height: 40px; background: #eee">
<!-- this simulates your web site's navigation -->
<div>Catalog</div>
<div>Rewards</div>
<div>etc</div>
</header>
<iframe id="bhpage" src="https://app.bubblehouse.com/blocks/v2023061/YOUR_SITE_SLUG_HERE/Rewards7?instance=bhpage&auth=YOUR_USER_TOKEN_HERE" sandbox="allow-top-navigation allow-scripts allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin" allow="clipboard-write" style="border: 0; width: 100%; height: 1500px;"></iframe>
<script>
window.addEventListener('message', function (ev) {
let data = ev.data;
if (!data) return;
let call = data.bubblehouseBlockClientCall;
if (!call) return;
let instance = data.instance;
let el = document.getElementById(instance);
if (el) {
console.log(`[BubbleSample] BlockClient.${call} from ${instance}:`, data);
el.dispatchEvent(new CustomEvent('bhclient:' + call, { detail: data, bubbles: true }));
} else {
console.warn(`[BubbleSample] invalid call BlockClient.${call} from unknown instance ${instance}:`, data);
}
});
document.addEventListener('bhclient:Resize1', function (ev) {
ev.target.style.height = ev.detail.height + 'px';
});
document.addEventListener('bhclient:OpenPopup1', function (ev) {
openPopup(ev.detail);
});
let lastInstanceId = 0;
function openPopup(options) {
let instance = `popup-${options.name}-${++lastInstanceId}`;
let url = new URL(options.url);
url.searchParams.set('instance', instance);
let dialog = document.createElement('dialog');
dialog.id = instance + '-dialog';
dialog.style = options.dialogStyle;
dialog.addEventListener('click', closeOnClickOutside)
dialog.addEventListener('close', function () { document.body.removeChild(dialog) })
let blockFrame = document.createElement('iframe');
blockFrame.id = instance;
blockFrame.src = url.toString();
blockFrame.style = options.frameStyle;
blockFrame.addEventListener('bhclient:ClosePopup1', function () { dialog.close() });
dialog.appendChild(blockFrame);
document.body.appendChild(dialog);
dialog.showModal();
function closeOnClickOutside(ev) {
const d = dialog.getBoundingClientRect()
if (ev.clientX < d.left || ev.clientX > d.right || ev.clientY < d.top || ev.clientY > d.bottom) {
dialog.close()
}
}
}
</script>
</body>
</html>