Svante
Svante

Reputation: 1069

How to work around document.getElementById() usage in JS libs

I'm trying to integrate a Javascript library in my Polymer 1.7 element which uses document.getElementById(divId);. My Polymer element looks something like this:

<template>
  <div id="libDiv"></div>
  <button on-tap="go">Go!</button>
</template>
<script src="https://some-provider.com/greatlib.js"></script>
<script>
Polymer({
  is: 'lib-test',
  go: function () {
    GreatLib.render('libDiv');
  }
});
</script>

The library greatlib.js could look something like this:

var GreatLib = (function() {
  var render = function(divId) {
    document.getElementById(divId).innerHTML = 'GreatLib is great!';
  };
  return {render: render};
}());

Of cause document.getElementById(divId) can't find my div because it is in a Shadow DOM and not on the main document.

Is there a way to work around this and let document.getElementById(divId) find elements in my Polymer element, without modifying "GreatLib"?

Run the following example in Chrome:

<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<!--Use shadow DOM if available -->
<script>
  window.Polymer = {
    dom: 'shadow'
  };
</script>
<link href="polymer/polymer.html" rel="import">

<dom-module id="x-example">
  <style></style>
  <template>
  <div id="libDiv"></div>
  <button on-tap="go">Go!</button>
  </template>
  <script>
    var GreatLib = (function() {
      var writeTextToDiv = function(divId) {
        console.log('divId', divId);
        console.log('document.getElementById(divId)', document.getElementById(divId));
        document.getElementById(divId).innerHTML = 'GreatLib is great!';
      };
      return {
        writeTextToDiv: writeTextToDiv
      };
    }());
    
    Polymer({
      is: 'x-example',
      go: function () {
        GreatLib.writeTextToDiv('libDiv');
      }
    });
  </script>
</dom-module>

<x-example></x-example>

Upvotes: 1

Views: 955

Answers (1)

Kaiido
Kaiido

Reputation: 136756

I don't know Polymer nor shadowDOM, and I took the opportunity of this question as an excuse to learn a bit on it.

So first of, it doesn't seem like a great idea to use ids for getting your shadowDOM. Also, as a reminder, shadowDOM is a way to hide the structure of your page.

But here is one Q/A that helped me to come to the conclusion that nope, document.getElementyId is not the right tool out-of-the box.

However, you can access it thanks to document.querySelector and the ::shadow selector so here is an horrible hack that should probably not be used by anyone but which seems to work :

(function(){
  // first check selector support
  try{
     document.querySelector('::shadow');
    }
  catch(e){
    return; // not supported don't change anything
    }
  var gbi = document.getElementById.bind(document);
  document.getElementById = function(_id){
    var el = gbi(_id); // first try the original one
    if(el){
      return el;
      }
    else // nothing, try in the shadow
      return document.querySelector('*::shadow #' + _id);
   }
  })()
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<!--Use shadow DOM if available -->
<script>
  window.Polymer = {
    dom: 'shadow'
  };
</script>
<link href="polymer/polymer.html" rel="import">

<dom-module id="x-example">
  <style></style>
  <template>
  <div id="libDiv"></div>
  <button on-tap="go">Go!</button>
  </template>
  <script>
    var GreatLib = (function() {
      var writeTextToDiv = function(divId) {
        console.log('divId', divId);
        console.log('document.getElementById(divId)', document.getElementById(divId));
        document.getElementById(divId).innerHTML = 'GreatLib is great!';
      };
      return {
        writeTextToDiv: writeTextToDiv
      };
    }());
    
    Polymer({
      is: 'x-example',
      go: function () {
        GreatLib.writeTextToDiv('libDiv');
      }
    });
  </script>
</dom-module>

<x-example></x-example>

Upvotes: 1

Related Questions