tommy
tommy

Reputation: 65

Can't catch a click event on a button in typescript

Can't catch a click event on a button in typescript. I have very simple set up: a button in index.html and a short code that tries to detect whether or not the button was clicked, but it is not working. Here are these two files:

index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Typescript App</title>
  </head>
  <body>
    <div id="main" class="main">
      <div id="display">
        <button id="testSmth">Test Something</button>
      </div>
    </div>
    <script src="./index.ts"></script>
  </body>
</html>

And here is index.ts:

console.log('Starting')

class TestSmth {
    testSmthBut: HTMLObjectElement | null;
    clicked: boolean;

    constructor(){
        this.testSmthBut = document.querySelector('#testSmth');
        this.clicked = false;
        this.testSmthBut?.addEventListener('click', this.clickHandler);

    }

    clickHandler = (): void => { 
        this.clicked = true;
        console.log('clicked');
    }​;​
}

function sleep(milliseconds: number): void {
    const date = Date.now();
    let currentDate = null;
    do {
      currentDate = Date.now();
    } while (currentDate - date < milliseconds);
}

function lookigForClick(foo: TestSmth): void {
    if (foo.clicked === true) {
        console.log('yehhhh!!!');
        return;
    }
    else {
        console.log('still waiting');
        sleep(1000);
        lookigForClick(foo);
    }
} 

console.log('A')
let testsmth = new TestSmth();
lookigForClick(testsmth);

After compiling to js and starting a local server with node.js I can't get the recursion to stop no matter how often I clicked the button (see image below). What do I do wrong? Thanks :) enter image description here

EDIT:

  1. TS version 4.3.5
  2. Project structure:
-project_folder:
---tsconfig.json
---package.json
---src:
------index.ts
------index.html
  1. How to compile and start server: got to the project_folder and run
npm install
npm start
  1. package.json
{
  "name": "carcassone-parcel",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "parcel serve src/index.html",
    "watch": "parcel watch src/index.html"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^4.11.0",
    "@typescript-eslint/parser": "^4.11.0",
    "eslint": "^7.16.0",
    "eslint-config-prettier": "^7.1.0",
    "eslint-plugin-prettier": "^3.3.0",
    "parcel": "^1.12.4",
    "parcel-bundler": "^1.12.4",
    "prettier": "^2.2.1",
    "typescript": "^4.3.5"
  }
}
  1. tsconfig.json
{
    "compilerOptions": {
      "target": "es5",
      "module": "esnext",
      "lib": ["ESNext", "DOM"],
      "jsx": "react",
      "sourceMap": true,
      "strict": true,
      "moduleResolution": "node",
      "baseUrl": "./src",
      "paths": {
        "~/*": ["./*"]
      },
      "typeRoots": ["src/images/"],
      "allowSyntheticDefaultImports": true,
      "esModuleInterop": true
    },
    "include": ["src/**/*"]
}

Upvotes: 0

Views: 1400

Answers (1)

Alireza Ahmadi
Alireza Ahmadi

Reputation: 9923

The problem is that you've created an infinite loop. To make sense for you just run lookigForClick method in playground and see the browser is hanging.

This is playground link that is hanging because of infinite loop.

Solution

Instead of custom infinite loop use setInterval methods like this:

function lookigForClick(foo: TestSmth): void {
    if (foo.clicked === true) {
        console.log('yehhhh!!!');
        return;
    }
    else {
        console.log('still waiting');
    }
} 
console.log('A');
let testsmth = new TestSmth();

window.setInterval(()=>{
    lookigForClick(testsmth)
},1000)

Here is StackBlitz working sample that I created for you.

Update

It seems you need custom infinite loop. So you have two options:

1.sleep methods must be async to prevent browser freezing. Note that In this way you don't really wait for another action you just have working infinite loop.

function delay(ms: number) {
    return new Promise( resolve => setTimeout(resolve, ms) );
}

And then use it in lookigForClick methods like this:

function lookigForClick(foo: TestSmth): void {
    if (foo.clicked === true) {
        console.log('yehhhh!!!');
        return;
    }
    else {
      (async () => { 
        console.log('still waiting');

         await delay(1000);

        lookigForClick(foo);
      })();   
    }
}

PlaygroundLink

  1. Simple and better way is create actual waiting methods. For example if I call sleep(10000) I have to wait 10 seconds to execute next line code!

Again Note that your sleep method didn't actually wait. To test that just call sleep(10000) and see immediately console.log('still waiting'); executed.

So to have actual wait method simply use window.setTimeout like this:

function lookigForClick(foo: TestSmth): void {
    if (foo.clicked === true) {
        console.log('yehhhh!!!');
        return;
    }
    else {
      window.setTimeout(()=>{
        console.log('still waiting');

        lookigForClick(foo);
      }, 10000);   
    }
}  

Now run this Playground link and see after each 10 seconds this console.log('still waiting'); executed.

Upvotes: 1

Related Questions