Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

hubspot chat interface is missing in Turbolinks visited page, but working for full page refresh

I want to integrade hubspot chat interface in my Rails 4 + Turbolinks application. I have configured Google Tag Manager to show support chat interface in each page load event which is working fine.

GTM tag (custom html)

<!-- Start of HubSpot Embed Code -->
<script type="text/javascript" id="hs-script-loader" async defer src="//js.hs-scripts.com/xxxxxx.js"></script>
<!-- End of HubSpot Embed Code -->

PROBLEM

  • When I refresh a page manually (full page refresh) hubspot client interface (chat bubble window) shows up.
  • when I visit page with Turbolinks it doesn't show up.

For debugging I have followed

how-do-i-know-if-my-hubspot-tracking-code-is-working

I can see this tag added to document body (DOM) by inspecting element or by checking it into browser's view page source. Also Network tab show the network call done to js.hs-script.com.

EXPECTED

  • hubspot chat interface to work with turbolinks page visit:

hubspot chat client interface

like image 628
przbadu Avatar asked Jul 06 '18 05:07

przbadu


2 Answers

I had this issue as well and solved it by removing any existing Hubspot scripts from the header before (re-)including the Hubspot script loader. I just added the following script in my view directly before adding the Hubspot loader:

<script>
  // Remove Hubspot <head> scripts after a Turbolinks page load
  // so it can re-initialize properly.  Paste this code immediately
  // before the hubspot loading script in the <body>
  (function() {
    var scripts, script;
  
    // Remove scripts added by hs-scriptloader
    scripts = document.querySelectorAll('script[data-loader="hs-scriptloader"]');
    for (var script of scripts) {
      script.remove();
    }
  
    // Remove Hubspot Analytics
    script = document.getElementById('hs-analytics');
    if (script != undefined) {
      script.remove();
    }
  
    // Avoid duplicate chat warning and allow chat to load
    window.hubspot_live_messages_running = undefined
  })()
</script>

<!-- Now add the script to load Hubspot-->
<script async="" defer="defer" id="hs-script-loader" src="//js.hs-scripts.com/xxxxxxx.js" type="text/javascript"></script>

It's still a brittle solution since Hubspot could change the selectors (data-loader etc.) at any time.

like image 168
Tron Avatar answered Oct 18 '22 12:10

Tron


The hubspot code inserts 2 js files into the <head> and injects some inline stylesheets into the <body> - one of the js files (conversations-embed.js) creates a div with the id #hubspot-messages-iframe-container. As its name suggests, it contains an iframe with the chat window. It also adds a bunch of css to the body.

The main problem here is that Turbolinks replaces everything in the <body>per page change, leaving the hubspot script unaware of the fact that a page has been changed. Since everything is replaced, the div injected by the script and the css disappears.

My first try was to clone the chat container div into a window variable, and use appendChild(clonedNode) to the event.data.newBody element within the turbolinks:before-render event. While this kinda works, it isn't very elegant. It disappears and reappears each time you change a page. Also, the state was not carried over so if I closed the window it would be open at the next page and so on.

What does work is to move the #hubspot-messages-iframe-containerout of the body into the html tag itself, leaving it out of harms way from turbolinks. It's state is kept (open, closed, etc...) and does not blink or have any side effects. Remember that you have to manually remove it from the pages you may not want it on.

I know it's a bit hacky, but it works and until they have some sort of api to reinitialse the chat window this is a workaround:

document.addEventListener("turbolinks:load", function() {
  let targetNode = document.body
  let config = { childList: true }
  let callback = function() {
    if (document.body.querySelector('#hubspot-messages-iframe-container')) {
      document.documentElement.appendChild(
        document.getElementById('hubspot-messages-iframe-container')
      )
      observer.disconnect()
    }
  }

  let observer = new MutationObserver(callback)
  if (!document.querySelector("html > #hubspot-messages-iframe-container")) {
    observer.observe(targetNode, config)
  }
})

You will also need to copy the style injected into the body, since Turbolinks will also replace this. This is what I ended up with (converted to Sass):

html.hs-messages-widget-open.hs-messages-mobile
  height: 100% !important
  overflow: hidden !important
  position: relative !important
  body
    height: 100% !important
    overflow: hidden !important
    position: relative !important
    margin: 0 !important

#hubspot-messages-iframe-container
  display: initial !important
  z-index: 2147483647
  position: fixed !important
  bottom: 0 !important
  right: 0 !important
  &.internal
    z-index: 1016
    iframe
      min-width: 108px
  .shadow
    display: initial !important
    z-index: -1
    position: absolute
    width: 0
    height: 0
    bottom: 0
    right: 0
    content: ""
    &.internal
      display: none !important
    &.active
      width: 400px
      height: 400px
      background: radial-gradient(ellipse at bottom right, rgba(29, 39, 54, 0.16) 0, rgba(29, 39, 54, 0) 72%)
  iframe
    display: initial !important
    width: 100% !important
    height: 100% !important
    border: none !important
    position: absolute !important
    bottom: 0 !important
    right: 0 !important
    background: transparent !important
like image 30
Adam Cooper Avatar answered Oct 18 '22 12:10

Adam Cooper