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.
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/bootstrap@5.0.2/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/bootstrap@5.0.2/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
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>