title: Responsive Heatmaps
body: <h1>The Problem</h1>
  <p>I've encountered a slight problem where I've written <em>some</em> code where I've automated a script to track the users interactions with a UI, including <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event" rel="noreferrer"><code>mousemove</code></a> &amp; <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event" rel="noreferrer"><code>click</code></a> events. If I didn't have to worry about making it responsive, then I could probably call it a day &amp; ship the work that I've done already, however my brain is seeking some wizardry knowledge, I'm struggling to make it super responsive. Here's just a <em>quick</em> example of the kinda thing that I'm working on, it's nothing genius, if anything I it's mostly <a href="https://www.patrick-wied.at/static/heatmapjs/" rel="noreferrer">heatmap.js</a> that's doing the heavy lifting. Currently I'm just seeing if I can do this as a proof of concept more than anything else...</p>
  <p><a href="https://i.stack.imgur.com/8ZoM7.png" rel="noreferrer"><img src="https://i.stack.imgur.com/8ZoM7.png" alt="Heatmap example" /></a></p>
  <h2>The Code</h2>
  <p>So currently, I'm tracking the <code>event.pageX</code> &amp; <code>event.pageY</code> values to store <em>exactly</em> where an event took place, I'm also storing the <code>window.innerWidth</code> &amp; <code>window.innerHeight</code> values to <em>try</em> &amp; work out some function that would allow me to offset the positions based on the size(s) of other devices.</p>
  <p>E.g. If you look at the sample image above, that's perfect for a static page, but if I were to say make the page a little more narrow, you can see here that it's doesn't line up with the image above:</p>
  <p><a href="https://i.stack.imgur.com/93s3v.png" rel="noreferrer"><img src="https://i.stack.imgur.com/93s3v.png" alt="Heatmap offset example" /></a></p>
  <p>Anyway, without blabbering on too much, here's some sample code:</p>
  <pre class="lang-js prettyprint-override"><code>// A lot of other code...
  
  var setupHeatMaps = function (pages, heatMaps) {
    pages.forEach(function (page) {
      page.addEventListener(&quot;click&quot;, function (event) {
        heatMaps.push({ x: event.pageX, y: event.pageY, value: 10000 });
        onStateChange();  
      });
  
      // Don't collect ALL mouse movements, that'd be crazy, so collect 
      // every 1/10 mouse movements.
      var counter = 0;
  
      page.addEventListener(&quot;mousemove&quot;, function (event) {
        if (counter === 10) {
          heatMaps.push({ x: event.pageX, y: event.pageY, value: 20 });
          onStateChange();
          counter = 0;
        } else {
          counter ++;
        }
      });
    });
  };
  
  // A lot of other code... 
  
  // Curried function so that it can be passed around without exposing the state...
  var renderHeatMaps = function (heatMaps) {
    return function () {
      var max = heatMaps.length;
      var points = heatMaps;
  
      var parent = getParentElement();
      var styleObj = window.getComputedStyle(parent);
      var div = document.createElement(&quot;div&quot;);
      var body = document.querySelector(&quot;body&quot;);
      var background = document.createElement(&quot;div&quot;);
  
      // This element needs to sit in front of the 
      // background element, hence the higher z-index value.
      div.style.position = &quot;absolute&quot;;
      div.style.zIndex = 9;
      div.style.left = &quot;0px&quot;;
      div.style.top = &quot;-80px&quot;;
      div.style.width = &quot;100vw&quot;;
  
      // Even though this element will sit behind the element
      // that's created above, we will still want this element to 
      // sit in front of 99% of the content that's on the page.
      background.style.position = &quot;fixed&quot;;
      background.style.top = &quot;0px&quot;;
      background.style.left = &quot;0px&quot;;
      background.style.height = &quot;100vh&quot;;
      background.style.width = &quot;100vw&quot;;
      background.style.zIndex = 5;
      background.style.backgroundColor = &quot;rgba(255, 255, 255, 0.35)&quot;;
      background.setAttribute(&quot;id&quot;, &quot;quote-customer-heat-map-background&quot;);
  
      var heightInPx = styleObj.getPropertyValue(&quot;height&quot;);
      var rawHeight = parseInt(heightInPx.replace(&quot;px&quot;, &quot;&quot;));
      var newHeight = parseInt((rawHeight + 80));
  
      div.style.height = newHeight + &quot;px&quot;;
      div.setAttribute(&quot;id&quot;, &quot;quote-customer-heat-map-foreground&quot;);
      body.style.paddingBottom = &quot;0px&quot;;
      body.appendChild(background);
      body.appendChild(div);
  
      var heatMap = h337.create({
        container: div,
        radius: 45
      });
      
      heatMap.setData({ max: max, data: points });
    };
  };
  
  // A lot of other code...
  </code></pre>
  <p>As the <code>pages</code> elements that you can see being used in <code>setupHeatMaps</code> changes in width, viewing this data gets offset quite badly. I'll be honest, I've spent a <em>lot</em> of time yesterday thinking about this issue &amp; I've still not thought of anything that seems reasonable.</p>
  <h2>Alternatively</h2>
  <p>I have wondered if I should somehow <em>just</em> store the page as an image, with the heatmap overplayed, that way I wouldn't <em>really</em> have to worry about the heatmap being responsive. But then I need to figure out some other things... E.g. Versioning this data, so in the event that a user views a page on their phone, it'll stored that data separately to data that was collected from a previous session where they were on a laptop or on a desktop device.</p>
  <h1>Conclusion</h1>
  <p>I'll be honest, I'm not <em>entirely</em> sure what the best course of action is, have any of you guys encountered anything like this before? Have you guys thought of something genius that solves an issue like this?</p>
  <p><strong>P.S.</strong> I would share a lot more code, however there's an <em>immense</em> amount of code that goes into this <em>overall</em> solution, so I kinda can't share all of it, I'm pretty sure the guys at Stackoverflow would hate me for that!  - You can also tell that I'm doing this as a POC because normally I'd just offload the rendering of the <code>background</code> element &amp; the <code>div</code> element to the underlying framework rather than do it programatically like this, keep that in mind, this <em>is just a <strong>POC</strong></em>.</p>