javascriptclock

The js seems like stop working when I switch to another tab on browser, is a problem?


I made a clock. I realized that when I switch another tab and came back after a while the clock stop behind current time. I found something named web workers, is it related to this?? Same senteces, don't read. I made a clock. I realized that when I switch another tab and came back after a while the clock stop behind current time. I found something named web workers, is it related to this??

I expected the code work anytime but it didn't. The code:

let numbers = document.getElementsByClassName('number');
let numbersLength = numbers.length-1; 
let numberContainer = document.querySelectorAll('.number > div');
let minute = document.querySelector('#minute');
let hour = document.querySelector('#hour');
let second = document.querySelector('#second');
let startSecond = new Date().getSeconds()*6;
let startHour = new Date().getHours()*30;
let startMinute = new Date().getMinutes()*6;


setInterval(increaseSecond, 1000);

function readyHour() {
    //second starting position
    second.style.transform = `rotate(${startSecond}deg)`;
    minute.style.transform = `rotate(${startMinute}deg)`;
    hour.style.transform = `rotate(${startHour}deg)`;
    
    //numbers positions
    for(let i = 0; i <= numbersLength; i++){
        numbers[i].style.transform = `rotate(${i*30}deg)`;
        numberContainer[i].style.transform = `rotate(${-(i*30)}deg)`;
    }
}
readyHour();

function increaseSecond(){
    let currentSecond = new Date().getSeconds();
    let currentMinute = new Date().getMinutes();
    let currentHour = new Date().getHours();

    second.style.transform = `rotate(${currentSecond*6}deg)`;
    if(currentSecond === 0){
        minute.style.transform = `rotate(${currentMinute*6}deg)`;
    };

    if(currentMinute === 0)
        hour.style.transform = `rotate(${currentHour*30}deg)`;
        
}
*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body{
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}
.clock{
    background-color: black;
    height: 350px;
    width: 350px;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 50%;
    position: relative;
    border: 8px solid #31e1e7;
}

.number{
    font-size: 3rem;
    color: #ffffff;
    text-align: center;
    position: absolute;
    height: 100%;
    width: 100%;
}

.hand{
    position: absolute;
    left: 49.2%;
    bottom: 50%;
    width: 3px;
    background-color: red;
    transform-origin: bottom;
}
#second{
    z-index: 10;
    height: 120px;
}
#minute{
    height: 110px;
}

#hour{
    height: 65px;
}

#clock-center{
    background-color: #ffffff;
    height: 15px;
    width: 15px;
    z-index: 11;
    border-radius: 50%;
    position: absolute;
}
<!DOCTYPE html>
<html>
<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 rel="stylesheet" href="assets/main.css">
</head>
<body>
    
</head>
<body>
    <div class="clock">
        <div id="clock-center"></div>
        <div class="hand " id="minute"></div>
        <div class="hand " id="hour"></div>
        <div class="hand" id="second"></div>
        
        <div class="number"><div>12</div></div>
        <div class="number"><div>1</div></div>
        <div class="number"><div>2</div></div>
        <div class="number"><div>3</div></div>
        <div class="number"><div>4</div></div>
        <div class="number"><div>5</div></div>
        <div class="number"><div>6</div></div>
        <div class="number"><div>7</div></div>
        <div class="number"><div>8</div></div>
        <div class="number"><div>9</div></div>
        <div class="number"><div>10</div></div>
        <div class="number"><div>11</div></div>
    </div>
    
    <script src="assets/main.js"></script>
</body>
</html>


Solution

  • The problem you face is not so much related to the inactivity of the browser page and that timer ticks are suspended. Once the page comes into view again, these ticks resume immediately, so there should be opportunity for your code to display the clock correctly on those ticks.

    The problem is really caused by the fact that your code only updates the minute hand when a minute has passed, and the hour hand only updates when an hour has passed. This practically means that when you come back to the page after a long period of inactivity, the minute and hour hand will not be updated until enough time has passed, and will appear wrong during that time.

    The solution is simple: always update the display of all three hands, unconditionally, at every tick of setInterval.

    So change this:

        if(currentSecond === 0){
            minute.style.transform = `rotate(${currentMinute*6}deg)`;
        };
    
        if(currentMinute === 0)
            hour.style.transform = `rotate(${currentHour*30}deg)`;
    

    to this:

        minute.style.transform = `rotate(${currentMinute*6}deg)`;
        hour.style.transform = `rotate(${currentHour*30}deg)`;
    

    This will fix the issue.

    More remarks

    I find it not intuitive that the hour hand moves with steps of 30 degrees. That means that when it is one minute to 12, the hour hand still points to 11, giving the false impression it is one minute to 11.

    To a lesser extent this also applies to the minute hand: it could move with smaller steps than 6 degrees giving a more fluent display.

    Here is how I would change it:

    const minute = document.querySelector('#minute');
    const hour = document.querySelector('#hour');
    const second = document.querySelector('#second');
    
    function setNumbers() {
        const numbers = document.getElementsByClassName('number');
        const numberContainer = document.querySelectorAll('.number > div');
        for (let i = 0; i < numbers.length; i++) {
            numbers[i].style.transform = `rotate(${i*30}deg)`;
            numberContainer[i].style.transform = `rotate(${-(i*30)}deg)`;
        }
    }
    
    function refresh() {
        const now = new Date();
        const currentSecond = now.getSeconds();
        const currentMinute = now.getMinutes();
        const currentHour   = now.getHours();
    
        second.style.transform = `rotate(${currentSecond*6}deg)`;
        minute.style.transform = `rotate(${currentMinute*6+currentSecond/10}deg)`;
        hour.style.transform   = `rotate(${currentHour*30 + currentMinute/2}deg)`;
    }
    
    setNumbers();
    refresh();
    setInterval(refresh, 1000);
    *{
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    body{
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100vh;
    }
    .clock{
        background-color: black;
        height: 350px;
        width: 350px;
        display: flex;
        justify-content: center;
        align-items: center;
        border-radius: 50%;
        position: relative;
        border: 8px solid #31e1e7;
    }
    
    .number{
        font-size: 3rem;
        color: #ffffff;
        text-align: center;
        position: absolute;
        height: 100%;
        width: 100%;
    }
    
    .hand{
        position: absolute;
        left: 49.2%;
        bottom: 50%;
        width: 3px;
        background-color: red;
        transform-origin: bottom;
    }
    #second{
        z-index: 10;
        height: 120px;
    }
    #minute{
        height: 110px;
    }
    
    #hour{
        height: 65px;
    }
    
    #clock-center{
        background-color: #ffffff;
        height: 15px;
        width: 15px;
        z-index: 11;
        border-radius: 50%;
        position: absolute;
    }
    <!DOCTYPE html>
    <html>
    <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 rel="stylesheet" href="assets/main.css">
    </head>
    <body>
        
    </head>
    <body>
        <div class="clock">
            <div id="clock-center"></div>
            <div class="hand " id="minute"></div>
            <div class="hand " id="hour"></div>
            <div class="hand" id="second"></div>
            
            <div class="number"><div>12</div></div>
            <div class="number"><div>1</div></div>
            <div class="number"><div>2</div></div>
            <div class="number"><div>3</div></div>
            <div class="number"><div>4</div></div>
            <div class="number"><div>5</div></div>
            <div class="number"><div>6</div></div>
            <div class="number"><div>7</div></div>
            <div class="number"><div>8</div></div>
            <div class="number"><div>9</div></div>
            <div class="number"><div>10</div></div>
            <div class="number"><div>11</div></div>
        </div>
        
        <script src="assets/main.js"></script>
    </body>
    </html>