1

I am working on a page that needs to insert a script into the DOM and evaluate it. This script contains a class that will instantiate some callbacks to particular elements that are also being inserted into the page.

If i do this process for one event handler, everything works as expected. However when i do this for a second event handler the first event handler stops working

function processJson(outputJson) {
  for (var i in outputJson) {
    let divIDForPlot = "plot_" + i;
    document.getElementById("parent").innerHTML += "<div id='" + divIDForPlot + "'></div>";

    let item = outputJson[i];
    let type = item["outputType"];

    if (type == "plot") {
      processPlot(divIDForPlot, item);
    } else if (type == "table") {

    }
    // break;

  }
}


function processPlot(elementId, plotData) {

  let additionalHtml = plotData["AdditionalHtml"];

  document.getElementById(elementId).innerHTML = additionalHtml;
  let script = plotData["script"];
  let outputData = " ";
  let dataString = "var data = " + JSON.stringify(outputData) + ";";
  let finalScript = dataString + "\n" + script;

  var s = document.createElement('script');
  s.type = 'text/javascript';
  try {
    s.appendChild(document.createTextNode(finalScript));
    document.getElementById(elementId).appendChild(s);
  } catch (e) {
    s.text = finalScript;
    document.getElementById(elementId).appendChild(s);
  }
}


function run() {
  processJson(dataJson["output"]);
}




let dataJson = {
  "output": [{

      "outputType": "plot",
      "AdditionalHtml": "<div id='revenue_trend'></div> <div class='control-row' >Periodicity:<select  id='revenue_trend_periodicity'></select>Measure:<select id = 'revenue_trend_measure' ></select ></div > ",
      "script": "class RevenueTrend{constructor(e){this.data=e,this.periodicitySelector=document.getElementById('revenue_trend_periodicity'),this.measureSelector=document.getElementById('revenue_trend_measure'),this.periodicitySelector.addEventListener('change',()=>{console.log('periodicitySelector changed')},!1),this.measureSelector.addEventListener('change',()=>{console.log('measureSelector changed')},!1),this.#b(['monthly','weekly'],this.periodicitySelector),this.#b(['revenue','profit','margin'],this.measureSelector)}updateBySelector=()=>{this.#a(this.periodicitySelector.value,this.measureSelector.value)};#a=(e,t)=>{var r=this.data.data[t][e];let i=this.data.layout[t]};#b=(e,t)=>{for(var r=0;r<e.length;r++){var i=document.createElement('option');i.text=e[r],t.appendChild(i)}}}let revenue_trend = new RevenueTrend(data);"
    },
    {

      "outputType": "plot",
      "AdditionalHtml": "<div id='revenue_trend2'></div> <div class='control-row' >Periodicity:<select  id='revenue_trend_periodicity2'></select>Measure:<select id = 'revenue_trend_measure2' ></select ></div > ",
      "script": "class RevenueTrend2{constructor(e){this.data=e,this.periodicitySelector=document.getElementById('revenue_trend_periodicity2'),this.measureSelector=document.getElementById('revenue_trend_measure2'),this.periodicitySelector.addEventListener('change',()=>{console.log('periodicitySelector2 changed')},!1),this.measureSelector.addEventListener('change',()=>{console.log('measureSelector2 changed')},!1),this.#b(['monthly','weekly'],this.periodicitySelector),this.#b(['revenue','profit','margin'],this.measureSelector)}updateBySelector=()=>{this.#a(this.periodicitySelector.value,this.measureSelector.value)};#a=(e,t)=>{};#b=(e,t)=>{for(var r=0;r<e.length;r++){var i=document.createElement('option');i.text=e[r],t.appendChild(i)}}}let revenue_trend2 = new RevenueTrend2(data);"
    }
  ]
};

run()
<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <div id="parent"></div>
</body>

</html>
Kival M
  • 182
  • 1
  • 10
  • 1
    It's going to be extremely difficult to figure out what's going on with an [mre] You should be able to create something similar with [Stack Snippets](https://meta.stackoverflow.com/q/358992). – Heretic Monkey Sep 29 '22 at 13:17
  • @mykaf `#b` is a private member of the class. It is not defined in this view, so I'm not sure how you can tell it's not a function... – Heretic Monkey Sep 29 '22 at 13:20
  • @HereticMonkey, I see what you mean. I'm a bit sleep-deprived and looking over my comment again - it doesn't make sense. – mykaf Sep 29 '22 at 13:24
  • @HereticMonkey I took a look at this and updated my answer to be a bit more verbose. The snippets are a big help. – Kival M Sep 29 '22 at 14:22
  • 1
    Where’s your event handlers? – Daniel A. White Oct 01 '22 at 13:34
  • 1
    @DanielA.White The event handlers are defined in the script that I am inserting – Kival M Oct 01 '22 at 13:40
  • Ok we really need an [mcve] – Daniel A. White Oct 01 '22 at 14:04
  • 4
    Duplicate of https://stackoverflow.com/questions/595808/is-it-possible-to-append-to-innerhtml-without-destroying-descendants-onclick-function-handler - your problem is running `document.getElementById("parent").innerHTML += …` multiple times – Bergi Oct 01 '22 at 15:26
  • 2
    Btw, [don't use `for…in` enumerations on arrays!](https://stackoverflow.com/q/500504/1048572) – Bergi Oct 01 '22 at 15:28

1 Answers1

3

The problem here is with this line

document.getElementById("parent").innerHTML += "<div id='" + divIDForPlot + "'></div>";

You need use appendChild to maintain the previous element's event listeners.

You can learn more about innerHTML vs appendChild here

function processJson(outputJson) {
  for (var i in outputJson) {
    let divIDForPlot = "plot_" + i;
    const plotElement = document.createElement('div');
    plotElement.id = divIDForPlot;
    document.getElementById("parent").appendChild(plotElement);

    let item = outputJson[i];
    let type = item["outputType"];

    if (type == "plot") {
      processPlot(divIDForPlot, item);
    } else if (type == "table") {

    }
    // break;

  }
}


function processPlot(elementId, plotData) {

  let additionalHtml = plotData["AdditionalHtml"];

  document.getElementById(elementId).innerHTML = additionalHtml;
  let script = plotData["script"];
  let outputData = " ";
  let dataString = "var data = " + JSON.stringify(outputData) + ";";
  let finalScript = dataString + "\n" + script;

  var s = document.createElement('script');
  s.type = 'text/javascript';
  try {
    s.appendChild(document.createTextNode(finalScript));
    document.getElementById(elementId).appendChild(s);
  } catch (e) {
    s.text = finalScript;
    document.getElementById(elementId).appendChild(s);
  }
}


function run() {
  processJson(dataJson["output"]);
}




let dataJson = {
  "output": [{

      "outputType": "plot",
      "AdditionalHtml": "<div id='revenue_trend'></div> <div class='control-row' >Periodicity:<select  id='revenue_trend_periodicity'></select>Measure:<select id = 'revenue_trend_measure' ></select ></div > ",
      "script": "class RevenueTrend{constructor(e){this.data=e,this.periodicitySelector=document.getElementById('revenue_trend_periodicity'),this.measureSelector=document.getElementById('revenue_trend_measure'),this.periodicitySelector.addEventListener('change',()=>{console.log('periodicitySelector changed')},!1),this.measureSelector.addEventListener('change',()=>{console.log('measureSelector changed')},!1),this.#b(['monthly','weekly'],this.periodicitySelector),this.#b(['revenue','profit','margin'],this.measureSelector)}updateBySelector=()=>{this.#a(this.periodicitySelector.value,this.measureSelector.value)};#a=(e,t)=>{var r=this.data.data[t][e];let i=this.data.layout[t]};#b=(e,t)=>{for(var r=0;r<e.length;r++){var i=document.createElement('option');i.text=e[r],t.appendChild(i)}}}let revenue_trend = new RevenueTrend(data);"
    },
    {

      "outputType": "plot",
      "AdditionalHtml": "<div id='revenue_trend2'></div> <div class='control-row' >Periodicity:<select  id='revenue_trend_periodicity2'></select>Measure:<select id = 'revenue_trend_measure2' ></select ></div > ",
      "script": "class RevenueTrend2{constructor(e){this.data=e,this.periodicitySelector=document.getElementById('revenue_trend_periodicity2'),this.measureSelector=document.getElementById('revenue_trend_measure2'),this.periodicitySelector.addEventListener('change',()=>{console.log('periodicitySelector2 changed')},!1),this.measureSelector.addEventListener('change',()=>{console.log('measureSelector2 changed')},!1),this.#b(['monthly','weekly'],this.periodicitySelector),this.#b(['revenue','profit','margin'],this.measureSelector)}updateBySelector=()=>{this.#a(this.periodicitySelector.value,this.measureSelector.value)};#a=(e,t)=>{};#b=(e,t)=>{for(var r=0;r<e.length;r++){var i=document.createElement('option');i.text=e[r],t.appendChild(i)}}}let revenue_trend2 = new RevenueTrend2(data);"
    }
  ]
};

run()
<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <div id="parent"></div>
</body>

</html>
Syed
  • 364
  • 1
  • 8