As of October 2024 the chat widget still appears to be negativley affecting CLS.
On the Chrome’s dev tools I recorded what happens when the chat is loaded onto the page using a slow connection and a throttled CPU. It looks as though an unidentifiable HTML node associated with the chat jumps from the top of the page down to the bottom, this is what I believe causes the CLS score to spike, even though nothing on the page appears to be impacted by this:
Unidentified node causing CLS spike
So I took this approach to fix the issue:
1. Added a placeholder HTML node to the page that looks like the chat icon. Below is the HTML and CSS used.
HTML
<div id="esChatPlaceholder" class="es-chat-placeholder"></div>
CSS
.es-chat-placeholder {
background: #d9342b url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' fill-rule='evenodd' viewBox='0 0 32 30'%3E%3Cpath d='M30.7 3.5Q30.25 1.1 28 .8S24.2.1 18.4.1 8.8.8 8.8.8c-1.2.2-2 .8-2.5 1.8 2.5-.3 5-.4 7.6-.4 5.7 0 9.4.7 10 .8 2.5.4 4.2 2.1 4.7 4.6.2.8.8 3.7.8 7.2s-.2 3.6-.3 4.9c1-.4 1.6-1.3 1.8-2.5 0 0 .7-3.1.7-6.8s-.7-6.8-.7-6.8m-4.7 4.3q-.45-2.4-2.7-2.7s-3.8-.7-9.6-.7-9.6.7-9.6.7c-1.5.2-2.5 1.2-2.7 2.7 0 0-.7 3.1-.7 6.8s.7 6.8.7 6.8q.45 2.4 2.7 2.7s2.4.5 6.4.6l2.4 4.1c.3.4.8.6 1.3.3.1 0 .3-.2.3-.3l2.4-4.1 6.4-.6C25 24 26 23 26.2 21.5c0 0 .7-3.1.7-6.8s-.7-6.8-.7-6.8'/%3E%3C/svg%3E") 50%/32px 32px no-repeat;
border-radius: 100%;
bottom: 15px;
box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 4px, rgba(0, 0, 0, 0.2) 0px 2px 12px;
height: 62px;
left: 15px;
position: fixed;
width: 62px;
z-index: 9000;
}
@media (pointer: coarse) {
.es-chat-placeholder {
bottom: 7px;
left: 7px;
}
}
2. Because the CLS issue was only happening on mobile devices, I used the below JavaScript functionality to load the chat functionality differently, depending on whether or not the user is on a mobile or desktop device.
For all devices: The URL fragment “'#show-chat” is added to the URL and then the chat is loaded. You will need to configure your chat inbox settings to look for the “#show-chat” URL fragment before calling the chat load functionality:
Wildcard match
Doing the above will stop the chat from loading until “
#show-chat” is added to the URL.
For desktop devices: Scrolling, moving the mouse, or clicking, causes the chat to be loaded onto the page.
For mobile devices: The placeholder itself has to be clicked before the chat is loaded onto the page.
Below is the JavaScript I used:
(function(win, doc)
{
'use strict';
var loadChatCounter = 0,
isTouchDevice = function()
{
return ('ontouchstart' in win) || (win.navigator.maxTouchPoints > 0) || (win.navigator.msMaxTouchPoints > 0);
},
loadChatWidget = function()
{
var urlFragment = '#show-chat',
locationHref = win.location.href,
containsURLfragment = locationHref.indexOf(urlFragment);
if (typeof win.HubSpotConversations === 'undefined' || (containsURLfragment !== -1 && loadChatCounter === 0)) {
return;
}
var mobileDevice = isTouchDevice() === true && (win.innerWidth < 768),
placeHolder = doc.getElementById('esChatPlaceholder'),
loadHSChat = function()
{
var status = win.HubSpotConversations.widget.status();
if (status.pending || status.loaded) {
return;
}
if (containsURLfragment === -1) {
win.history.replaceState(null, doc.title, locationHref + urlFragment);
}
if (loadChatCounter === 0) {
win.HubSpotConversations.widget.load();
loadChatCounter++;
if (mobileDevice === true && placeHolder !== null) {
win.HubSpotConversations.widget.open();
placeHolder.removeEventListener('click', loadHSChat, false);
}
if (placeHolder !== null) {
placeHolder.parentNode.removeChild(placeHolder);
}
}
};
if (mobileDevice === true && placeHolder !== null) {
placeHolder.addEventListener('click', loadHSChat, false);
}
if (mobileDevice === false) {
loadHSChat();
}
},
eventListener = function()
{
if (loadChatCounter === 0) {
loadChatWidget();
} else if (loadChatCounter > 0) {
// Removed event listeners after the chat widget loaded
doc.removeEventListener('click', eventListener, false);
win.removeEventListener('mousemove', eventListener, false);
win.removeEventListener('scroll', eventListener, false);
}
};
// Bind the initialisation of the chat to whatever event listeners you want, I chose these 3:
doc.addEventListener('click', eventListener, false);
win.addEventListener('mousemove', eventListener, false);
win.addEventListener('scroll', eventListener, false);
})(
window,
window.document
);
Implementing this improved our CLS score within a few days (it was 0.15, on average, prior to taking this screenshot):
CLS average improvement
The long and short of it, uers on mobile devices will not be exposed to the CLS conditions unless they actually want to load the chat (by clicking the placeholder). I Hope this helps somebody!
I should add that loading the chat within a custom HTML node via the “inlineEmbedSelector” JavaScript property could make a difference as localising the associated layout shift to a descendent node, which you can style, could have a positive impact on your website’s CLS score:
(function(win)
{
window.hsConversationsSettings = {
loadImmediately: false,
inlineEmbedSelector: '#some-id',
enableWidgetCookieBanner: true,
disableAttachment: true
};
})(window);