1

I am trying to create stopwatch for multiple users with php and javascript, i am using mysql DB for users. When i click on on user the stopwatch will start but for others it is not working. I tried with loops also but not solved.

this is wep page screenshoot

Below is my code for PHP and JS.

JS

        let seconds = 00;
        let tens = 00;
        let mins = 00;
        let getSeconds = document.querySelector('.seconds');
        let getTens = document.querySelector('.tens');
        let getMins = document.querySelector('.mins');
        let btnStart = document.querySelector('.btn-start');
        // let btnStop = document.querySelector('.btn-stop');
        let btnReset = document.querySelector('.btn-reset');
        let interval;

        btnStart.addEventListener('click', () => {
            clearInterval(interval);
            interval = setInterval(startTimer, 10);
            console.log(interval);
        })
        // btnStop.addEventListener('click', () => {
        //     clearInterval(interval);
        // })
        btnReset.addEventListener('click', () => {
            clearInterval(interval);
            tens = '00';
            seconds = '00';
            mins = '00';
            getSeconds.innerHTML = seconds;
            getTens.innerHTML = tens;
            getMins.innerHTML = mins;
        })

        function startTimer(){
            tens++;
            if(tens <= 9){
                getTens.innerHTML = '0' + tens;
            }
            if(tens > 9){
                getTens.innerHTML = tens;
            }
            if(tens > 99){
                seconds++;
                getSeconds.innerHTML = '0' + seconds;
                tens = 0;
                getTens.innerHTML = '0' + 0;
            }
            if(seconds > 9){
                getSeconds.innerHTML = seconds;
            }
            if(seconds > 59){
                mins++;
                getMins.innerHTML = '0' + mins;
                seconds = 0;
                getSeconds.innerHTML = '0' + 0;
            }
            if(mins > 9){
                getSeconds.innerHTML = mins;
            }
        }

PHP

<?php
include("config.php");
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
    <div class="card">
        <div class="card-header">
            <h4> Patient Detailes </h4>
        </div>

        <div class="card-body ">
            <div class="table-responsive ">
                <table class="table table-bordered table-striped tablewatch">
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Name</th>
                            <th>Age</th>
                            <th>Phone</th>
                            <th>Waiting Time</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php
                            $sql = "SELECT * FROM patient_detailes";
                            $query = mysqli_query($conn, $sql);
                            if($num = mysqli_num_rows($query)>0){
                                while($row = mysqli_fetch_array($query)){
                                    ?>
                        <tr>
                            <th scope="row"><?= $row['id'] ?></th>
                            <td><?= $row['name'] ?></td>
                            <td><?= $row['age'] ?></td>
                            <td><?= $row['phone'] ?></td>
                            <td><div class="wrapper">
                                    <button class="btn-start">Start</button>
                                    <!-- <button class="btn-stop">Stop</button> -->
                                    <button class="btn-reset">Reset</button>
                                    <p>
                                        <span class="mins">00</span>:<span class="seconds">00</span>:<span class="tens">00</span>
                                    </p> 
                                </div>
                            </td>
                        </tr>
                                    <?php
                                }
                            }
                        ?>
                       
                    </tbody>

                  </table>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/jquery-3.6.1.min.js" integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>
    <script src="script.js"></script>
</body>
</html>

Can anyone help me on this? TIA

4
  • Most likely because you're only attaching the event listeners to the first button. querySelector only returns the first instance, you might want querySelectorAll. I'm guessing here, not sure if that's the problem. Commented Aug 28, 2022 at 18:33
  • this is not working Commented Aug 29, 2022 at 4:32
  • "this is not working": what are you referring to? To your question? Commented Aug 29, 2022 at 7:21
  • I want to run stopwatch to each table row that take dynamically data from database . Commented Aug 29, 2022 at 7:57

1 Answer 1

2

The problem is that you are assigning your event listeners to a solitary set of buttons within the table by using querySelector rather than to them all. You could assign a listener to all buttons by iterating through the the collection returned by document.querySelectorAll() but an easier & cleaner approach would be to assign a delegated event listener to a common parent element that processes events registered upon it and delegates to whatever the intended target is to perform the task.

You might wish to look at MDN's introduction to events and event delegation to get a fuller picture of what's involved as they will explain it better than I.

The table seems like a good choice as the delegated target and, by adding a new className to each button ( such as timing-bttn as below ) you can identify which element has invoked the event by inspecting the event.target property.

If you also modify the HTML that you generate within the PHP loop so that the div.wrapper element has a new dataset attribute, such as data-id, like this:

<?php
    $sql = "SELECT * FROM patient_detailes";
    $query = mysqli_query($conn, $sql);
    if( $num = mysqli_num_rows($query) > 0 ){
        while($row = mysqli_fetch_array($query)){
?>
<tr>
    <th scope="row"><?=$row['id']?></th>
    <td><?=$row['name']?></td>
    <td><?=$row['age']?></td>
    <td><?=$row['phone']?></td>
    <td>
        <div data-id='<?=$row['id']?>' class="wrapper"><!-- note the data-id attribute?¬ -->
            <button class="btn-start">Start</button>
            <button class="btn-reset">Reset</button>
            <p>
                <span class="mins">00</span>:<span class="seconds">00</span>:<span class="tens">00</span>
            </p> 
        </div>
    </td>
</tr>

The benefit of this data-id attribute is that we can now store, within the global variable _timers, the reference to the setInterval call in relation to the data-id value ( assuming that each of these will be unique ) so the timers will be independently accessible.

Then some potential rendered HTML might appear like depicted in the snippet:

// utility / helper functions
const d = document;
const q = (e, n = d) => n.querySelector(e);
const qa = (e, n = d) => n.querySelectorAll(e);
//--------------------------------------
const _TIME_INTERVAL = 10;
// global variable to store references to each timer started
// so that they can be controlled separately.
const _timers = {};


// set the span values to the original string values
const initialise = (m, s, t) => {
  m.textContent = '00';
  s.textContent = '00';
  t.textContent = '00';
};


const startTimer = (m, s, t) => {
  // ensure display is set
  initialise(m, s, t);
  // find the numeric value from the spans
  let mins = Number(m.textContent);
  let secs = Number(s.textContent);
  let tens = Number(t.textContent);

  // return the timer that updates the individual spans
  return setInterval(() => {
    tens++;

    if (tens > 99) {
      secs++;
      tens = 0;
    }
    if (secs > 59) {
      mins++;
      secs = 0;
    }

    m.textContent = pad(mins);
    s.textContent = pad(secs);
    t.textContent = pad(tens);
  }, _TIME_INTERVAL);
};

// shorthand method to add zero to single digit
const pad = function(i) {
  return (parseInt(i) < 10) ? '0' + parseInt(i) : parseInt(i);
};











/*
  Rather than assign the same event handler to each and every button
  assign a "Delegated Event Listener" to a common parent ( the Table )
  and respond to the click as appropriate to the logic within.
*/
q('table.tablewatch').addEventListener('click', e => {
  if (e.target != e.currentTarget && e.target.classList.contains('timing-bttn')) {
    // find the SPAN parent node, the P
    let p = q('p', e.target.parentNode);

    // Find the dataset ID assigned (with PHP!) to the button parent element
    let id = e.target.parentNode.dataset.id;

    // find the SPAN elements within this wrapper
    let mins = q('.mins', p);
    let secs = q('.seconds', p);
    let tens = q('.tens', p);

    // either start or stop the timer
    switch (e.target.textContent) {
      case 'Start':
        _timers[id] = startTimer(mins, secs, tens);
        break;

      case 'Reset':
        initialise(mins, secs, tens);
        if (!isNaN(_timers[id])) clearInterval(_timers[id]);
        break;
    }
  }
})
table {
  border: 1px solid grey;
  width: 80%;
  font-family: monospace;
}

td,
th {
  padding: 0.25rem;
  margin: 0.25rem;
  border: 1px dotted grey
}

th {
  background: darkgrey;
  color: white;
}

button {
  padding: 0.5rem;
  margin: auto 0.1rem;
  flex: 1;
}

tr td:nth-of-type(4) div.wrapper {
  display: flex
}
<table class="table table-bordered table-striped tablewatch">
  <thead>
    <tr>
      <th>Id</th>
      <th>Name</th>
      <th>Age</th>
      <th>Phone</th>
      <th>Waiting Time</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">23</th>
      <td>John Smith</td>
      <td>97</td>
      <td>0141 373 5780</td>
      <td>
        <div data-id=23 class="wrapper">
          <button class="timing-bttn btn-start">Start</button>
          <button class="timing-bttn btn-reset">Reset</button>
          <p>
            <span class="mins">00</span>:<span class="seconds">00</span>:<span class="tens">00</span>
          </p>
        </div>
      </td>
    </tr>

    <tr>
      <th scope="row">48</th>
      <td>Agnes Potter</td>
      <td>101</td>
      <td>0141 652 8544</td>
      <td>
        <div data-id=48 class="wrapper">
          <button class="timing-bttn btn-start">Start</button>
          <button class="timing-bttn btn-reset">Reset</button>
          <p>
            <span class="mins">00</span>:<span class="seconds">00</span>:<span class="tens">00</span>
          </p>
        </div>
      </td>
    </tr>

    <tr>
      <th scope="row">92</th>
      <td>Desdemona Nichols</td>
      <td>87</td>
      <td>0141 854 9685</td>
      <td>
        <div data-id=92 class="wrapper">
          <button class="timing-bttn btn-start">Start</button>
          <button class="timing-bttn btn-reset">Reset</button>
          <p>
            <span class="mins">00</span>:<span class="seconds">00</span>:<span class="tens">00</span>
          </p>
        </div>
      </td>
    </tr>
  </tbody>
</table>


To run all timers at page load

// utility / helper functions
const d = document;
const q = (e, n = d) => n.querySelector(e);
const qa = (e, n = d) => n.querySelectorAll(e);
//--------------------------------------
const _TIME_INTERVAL = 10;
// global variable to store references to each timer started
// so that they can be controlled separately.
const _timers = {};





const startTimer = (m, s, t) => {
  // ensure display is set
  initialise(m, s, t);
  
  // find the numeric value from the spans
  let mins = Number(m.textContent);
  let secs = Number(s.textContent);
  let tens = Number(t.textContent);

  // return the timer that updates the individual spans
  return setInterval(() => {
    tens++;

    if (tens > 99) {
      secs++;
      tens = 0;
    }
    if (secs > 59) {
      mins++;
      secs = 0;
    }

    m.textContent = pad(mins);
    s.textContent = pad(secs);
    t.textContent = pad(tens);
  }, _TIME_INTERVAL);
};

// shorthand method to add zero to single digit
const pad = function(i) {
  return (parseInt(i) < 10) ? '0' + parseInt(i) : parseInt(i);
};



// set the span values to the original string values
// modified to return current displayed values
const initialise = (m, s, t) => {
  let value={m:m.textContent,s:s.textContent,t:t.textContent};
  m.textContent = '00';
  s.textContent = '00';
  t.textContent = '00';
  return value;
};



/* 
  Changes made below only other than the removal of the "start" buttons.
  Query the dom to find all div.wrapper elements so that we can then find the
  various SPAN elements within the P.
  The global `_timers` variable is once again keyed with the `dataset.id`
  and the return of `startTimer` function.
*/
d.addEventListener('DOMContentLoaded',()=>{
  qa('table.tablewatch > tbody > tr > td > div.wrapper').forEach( div=>{
    let mins = q('p span.mins', div);
    let secs = q('p span.seconds', div);
    let tens = q('p span.tens', div);
    
    initialise( mins, secs, tens );
    _timers[ div.dataset.id ]=startTimer(mins, secs, tens);
  })
});

/*
  The stop buttons again use the `delegated listener` and work
  as previously. Added small piece of code that returns the current
  timer value when the button is clicked. This might be useful?!
*/
q('table.tablewatch').addEventListener('click', e=>{
  if (e.target.classList.contains('timing-bttn') ) {
    let p = q('p', e.target.parentNode);
    let id = e.target.parentNode.dataset.id;
    let mins = q('.mins', p);
    let secs = q('.seconds', p);
    let tens = q('.tens', p);
    
    
    if ( !isNaN(_timers[id] ) ) {
      let current=initialise( mins, secs, tens );
      clearInterval( _timers[id] );
      
      console.info( 
        'Timer "%s" was at "%s" before being stopped at "%s"', 
        id,
        Object.values( current ).join(':'),
        new Date()
      );
    }
  }
})
table {
  border: 1px solid grey;
  width: 80%;
  font-family: monospace;
}

td,
th {
  padding: 0.25rem;
  margin: 0.25rem;
  border: 1px dotted grey
}

th {
  background: darkgrey;
  color: white;
}

button {
  padding: 0.5rem;
  margin: auto 0.1rem;
  flex: 1;
}

tr td:nth-of-type(4) div.wrapper {
  display: flex
}
<table class="table table-bordered table-striped tablewatch">
  <thead>
    <tr>
      <th>Id</th>
      <th>Name</th>
      <th>Age</th>
      <th>Phone</th>
      <th>Waiting Time</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">23</th>
      <td>John Smith</td>
      <td>97</td>
      <td>0141 373 5780</td>
      <td>
        <div data-id=23 class="wrapper">
          <button class="timing-bttn btn-reset">Reset</button>
          <p>
            <span class="mins">00</span>:<span class="seconds">00</span>:<span class="tens">00</span>
          </p>
        </div>
      </td>
    </tr>

    <tr>
      <th scope="row">48</th>
      <td>Agnes Potter</td>
      <td>101</td>
      <td>0141 652 8544</td>
      <td>
        <div data-id=48 class="wrapper">
          <button class="timing-bttn btn-reset">Reset</button>
          <p>
            <span class="mins">00</span>:<span class="seconds">00</span>:<span class="tens">00</span>
          </p>
        </div>
      </td>
    </tr>

    <tr>
      <th scope="row">92</th>
      <td>Desdemona Nichols</td>
      <td>87</td>
      <td>0141 854 9685</td>
      <td>
        <div data-id=92 class="wrapper">
          <button class="timing-bttn btn-reset">Reset</button>
          <p>
            <span class="mins">00</span>:<span class="seconds">00</span>:<span class="tens">00</span>
          </p>
        </div>
      </td>
    </tr>
  </tbody>
</table>

Sign up to request clarification or add additional context in comments.

1 Comment

How do I make the stopwatch start automatically on page load? without using the start button on your code . i tried but did not work

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.