How to call a JavaScript class method from a HTML page

I have a Typescript class that dynamically adds and HTML element to a page after loading. It looks like:

const calcElement: string = `
  <div class="container">
    <span class="cc-title">Field</span>
    <div class="row">
      <div>
        <label for="dropdown">Options</label><br/>
        <select id="dropdown" onChange="calculateValues()">
          <option value="1" selected="selected">1</option>
          <option value="3">3</option>
        </select>
      </div>
    </div>`;

export class CalculatorComponent {

  let otherClass: OtherClass;

  constructor() {
    this.otherClass = new OtherClass;
    this.init();
  }

  init(): void {
    let element = document.getElementById('calculator');
    if (element) {
      element.innerHTML = calcElement;
    }
  }

  calculateValues(): void {
    let value: number | null = <HTMLInputElement>document.getElementById('dropdown') ? parseInt((<HTMLInputElement>document.getElementById('dropdown')).value) : null;
  
    if (!value) {
      return;
    }
    // do something
    this.otherClass.callFunction();
  }
}

new CalculatorComponent();

My HTML page:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Welcome to the calculator</title>
    <link rel="stylesheet" href="../dist/styles.css">
  </head>
  <body>
    <h4>Calculator</h4>
    <!-- calculator-begin -->
    <div id="calculator"></div>
    <!-- cable-calculator-end -->
    <script type="text/javascript" src="../dist/calculator.js" defer></script>
  </body>
</html>

And finally my Webpack config:

const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  mode: "production",
  entry: './src/index.ts',
  target: 'web', 
  module: {
    rules: [
      {
        test: /.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  output: {
    filename: 'calculator.js',
    path: path.resolve(__dirname, 'dist'),
  },
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "src/*.css",
          to: path.resolve(__dirname, 'dist/styles.css'),
        }
      ],
    }),
  ],
};

Everything compiles correctly and my element is added on screen. However none of the function calls on the element work and throw:

Uncaught ReferenceError: calculateValues is not defined

How can I call calculateValues function from my HTML?

Answer

You need to have an instance of the calculator component class to call the method on. Since you’re already creating your DOM using the component code, I’d recommend setting up the handler there rather than relying on the HTML attribute.

  init(): void {
    let element = document.getElementById('calculator');
    if (element) {
      element.innerHTML = calcElement;
      document.getElementById('#dropdown')
        .addEventListener('change', () => this.calculateValues());
    }
  }