JS script works on codepen but not in WordPress - javascript

Been trying to add a typewriter effect, it works great on codepen but not in WP. I've tried to add the code directly to and also to load it as a .js file from the theme's folder, I can see in page-source it's loaded, but the effect isn't executing.
I've checked in console and there's no errors, initially I got an "Uncaught TypeError: Cannot read property 'innerHTML'", so I moved it to my footer.
This is my code:
HTML
<div>
<span id="container">Testing </span> <span id="text"></span><div id="cursor"></div>
</div>
CSS
#container {
display: inline;
vertical-align: middle;
font-family: 'Poppins',Helvetica,Arial,Lucida,sans-serif!important;
font-weight: 500!important;
font-size: 45px!important;
color: #000000!important;
text-align: left!important;
line-height: 1.5em;
}
#text {
display: inline;
vertical-align: middle;
font-family: 'Poppins',Helvetica,Arial,Lucida,sans-serif!important;
font-weight: 500!important;
font-size: 45px!important;
color: #000000!important;
text-align: left!important;
height: 70px;
line-height: 1.5em;
}
#cursor {
display: inline-block;
vertical-align: middle;
width: 3px;
height: 50px;
background-color: #000000;
animation: blink .75s step-end infinite;
}
#keyframes blink {
from, to {
background-color: transparent
}
50% {
background-color: #000000;
}
}
JS
// List of sentences
var _CONTENT = [
"This",
"That",
"These",
"Those"
];
// Current sentence being processed
var _PART = 0;
// Character number of the current sentence being processed
var _PART_INDEX = 0;
// Holds the handle returned from setInterval
var _INTERVAL_VAL;
// Element that holds the text
var _ELEMENT = document.querySelector("#text");
// Cursor element
var _CURSOR = document.querySelector("#cursor");
// Implements typing effect
function Type() {
// Get substring with 1 characater added
var text = _CONTENT[_PART].substring(0, _PART_INDEX + 1);
_ELEMENT.innerHTML = text;
_PART_INDEX++;
// If full sentence has been displayed then start to delete the sentence after some time
if(text === _CONTENT[_PART]) {
// Hide the cursor
_CURSOR.style.display = 'none';
clearInterval(_INTERVAL_VAL);
setTimeout(function() {
_INTERVAL_VAL = setInterval(Delete, 50);
}, 1000);
}
}
// Implements deleting effect
function Delete() {
// Get substring with 1 characater deleted
var text = _CONTENT[_PART].substring(0, _PART_INDEX - 1);
_ELEMENT.innerHTML = text;
_PART_INDEX--;
// If sentence has been deleted then start to display the next sentence
if(text === '') {
clearInterval(_INTERVAL_VAL);
// If current sentence was last then display the first one, else move to the next
if(_PART == (_CONTENT.length - 1))
_PART = 0;
else
_PART++;
_PART_INDEX = 0;
// Start to display the next sentence after some time
setTimeout(function() {
_CURSOR.style.display = 'inline-block';
_INTERVAL_VAL = setInterval(Type, 100);
}, 200);
}
}
// Start the typing effect on load
_INTERVAL_VAL = setInterval(Type, 100);

Related

Show images in quiz javascript

I'm trying to create a quiz that tests users awareness of real and fake emails. What I want to do is have the question displayed at the top saying "Real or Fake", then have an image displayed underneath which the user needs to look at to decided if it's real or fake. There are two buttons, real and fake, and regardless of whether they choose the right answer I want to swap the original image with annotated version - showing how users could spot that it was fake or real.
But I'm not sure how to show the annotated version once the answer has been submitted. Could someone help?
function Quiz(questions) {
this.score = 0;
this.questions = questions;
this.questionIndex = 0;
}
Quiz.prototype.getQuestionIndex = function() {
return this.questions[this.questionIndex];
}
Quiz.prototype.guess = function(answer) {
if (this.getQuestionIndex().isCorrectAnswer(answer)) {
this.score++;
}
this.questionIndex++;
}
Quiz.prototype.isEnded = function() {
return this.questionIndex === this.questions.length;
}
function Question(text, choices, answer) {
this.text = text;
this.choices = choices;
this.answer = answer;
}
Question.prototype.isCorrectAnswer = function(choice) {
return this.answer === choice;
}
function populate() {
if (quiz.isEnded()) {
showScores();
} else {
// show question
var element = document.getElementById("question");
element.innerHTML = quiz.getQuestionIndex().text;
// show options
var choices = quiz.getQuestionIndex().choices;
for (var i = 0; i < choices.length; i++) {
var element = document.getElementById("choice" + i);
element.innerHTML = choices[i];
guess("btn" + i, choices[i]);
}
showProgress();
}
};
function guess(id, guess) {
var button = document.getElementById(id);
button.onclick = function() {
quiz.guess(guess);
populate();
}
};
function showProgress() {
var currentQuestionNumber = quiz.questionIndex + 1;
var element = document.getElementById("progress");
element.innerHTML = "Question " + currentQuestionNumber + " of " + quiz.questions.length;
};
function showScores() {
var gameOverHTML = "<h1>Result</h1>";
gameOverHTML += "<h2 id='score'> Your scores: " + quiz.score + "</h2>";
var element = document.getElementById("quiz");
element.innerHTML = gameOverHTML;
};
// create questions here
var questions = [
new Question("<img src= 'netflix_fake.jpg' />", ["Real", "Fake"], "Fake"),
new Question("<img src= 'dropbox_real.jpg' />", ["Real", "Fake"], "Real"),
new Question("<img src= 'gov_real.jpg' />", ["Real", "Fake"], "Real"),
new Question("<img src= 'paypal_fake.jpg' />", ["Real", "Fake"], "Fake"),
new Question("<img src= 'gmail.jpg' />", ["Real", "Fake"], "Fake")
];
//create quiz
var quiz = new Quiz(questions);
// display
populate();
body {
background-color: #538a70;
}
.grid {
width: 600px;
height: 500px;
margin: 0 auto;
background-color: #fff;
padding: 10px 50px 50px 50px;
border: 2px solid #cbcbcb;
}
.grid h1 {
font-family: "sans-serif";
font-size: 60px;
text-align: center;
color: #000000;
padding: 2px 0px;
}
#score {
color: #000000;
text-align: center;
font-size: 30px;
}
.grid #question {
font-family: "monospace";
font-size: 30px;
color: #000000;
}
.buttons {
margin-top: 30px;
}
#btn0,
#btn1,
#btn2,
#btn3 {
background-color: #a0a0a0;
width: 250px;
font-size: 20px;
color: #fff;
border: 1px solid #1D3C6A;
margin: 10px 40px 10px 0px;
padding: 10px 10px;
}
#btn0:hover,
#btn1:hover,
#btn2:hover,
#btn3:hover {
cursor: pointer;
background-color: #00994d;
}
#btn0:focus,
#btn1:focus,
#btn2:focus,
#btn3:focus {
outline: 0;
}
#progress {
color: #2b2b2b;
font-size: 18px;
}
<div class="grid">
<div id="quiz">
<h1>Can you spot the fake email?</h1>
<hr style="margin-bottom: 20px">
<p id="question"></p>
<div class="buttons">
<button id="btn0"><span id="choice0"></span></button>
<button id="btn1"><span id="choice1"></span></button>
</div>
<hr style="margin-top: 50px">
<footer>
<p id="progress">Question x of y</p>
</footer>
</div>
</div>
When user clicks button I trigger class and I add it second name, on second I have written to get swapped, I wrote you basically full project, and please read the whole comments, to understand logic
//Calling Elements from DOM
const button = document.querySelectorAll(".check");
const images = document.querySelectorAll(".image");
const answer = document.querySelector("h1");
//Declaring variable to randomly insert any object there to insert source in DOM Image sources
let PreparedPhotos;
//Our Images Sources and With them are its fake or not
//fake: true - yes its fake
//fake: false - no its real
const image = [
[
{
src:
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg/1200px-Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg",
fake: true
},
{
src:
"http://graphics8.nytimes.com/images/2012/04/13/world/europe/mona-lisa-like-new-images/mona-lisa-like-new-images-custom4-v3.jpg",
fake: false
}
],
[
{
src:
"https://cdn.shopify.com/s/files/1/0849/4704/files/Creacion_de_Adan__Miguel_Angel_f5adb235-bfa8-4caa-8ffb-c5328cbad953_grande.jpg?12799626327330268216",
fake: false
},
{
src:
"https://cdn.shopify.com/s/files/1/0849/4704/files/First-image_Fb-size_grande.jpg?10773543754915177139",
fake: true
}
]
];
//Genrating Random Photo on HTML
function setRandomPhoto() {
//Random Number which will be length of our array of Object
//if you array includes 20 object it will generate random number
// 0 - 19
const randomNumber = Math.floor(Math.random() * image.length);
//Decalaring our already set variable as Array Object
PreparedPhoto = image[randomNumber];
//Our first DOM Image is Variables first object source
images[0].src = PreparedPhoto[0].src;
//and next image is next object source
images[1].src = PreparedPhoto[1].src;
}
//when windows successfully loads, up function runs
window.addEventListener("load", () => {
setRandomPhoto();
});
//buttons click
//forEach is High Order method, basically this is for Loop but when you want to
//trigger click use forEach - (e) is single button whic will be clicked
button.forEach((e) => {
e.addEventListener("click", () => {
//decalring variable before using it
let filtered;
//finding from our DOM image source if in our long array exists
//same string or not as Image.src
//if it exists filtered variable get declared with that found obect
for (let i = 0; i < image.length; i++) {
for (let k = 0; k < 2; k++) {
if (image[i][k].src === images[0].src) {
filtered = image[i][k];
}
}
}
//basic if else statement, if clicked button is Fake and image is true
//it outputs You are correct
//if clicked button is Real and Image is false it outputs Correct
//Else its false
//Our image checking comes from filtered variable
if (e.innerText === "Fake" && filtered.fake === true) {
answer.innerText = "You Are Correct";
images.forEach((image) => {
image.classList.toggle("hidden");
});
} else if (e.innerText === "Real" && filtered.fake === false) {
answer.innerText = "You Are Correct";
images.forEach((image) => {
image.classList.toggle("hidden");
});
} else {
answer.innerHTML = "You are Wrong";
images.forEach((image) => {
image.classList.toggle("hidden");
});
}
});
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
width: 100%;
min-height: 100vh;
display: flex;
justify-content: space-around;
align-items: center;
flex-direction: column;
}
.image-fluid {
display: flex;
}
.image-fluid .image {
width: 200px;
margin: 0 10px;
transition: 0.5s;
}
.image-fluid .image:nth-child(1).hidden {
transform: translateX(110px);
}
.image-fluid .image:nth-child(2).hidden {
transform: translateX(-110px);
}
<div class="container">
<div class="image-fluid">
<img src="" class="image hidden">
<img src="" class="image hidden">
</div>
<div class="button-fluid">
<button class="check">Fake</button>
<button class="check">Real</button>
</div>
</div>
<h1></h1>

JavaScript - Uncaught TypeError: totype[totypeIndex] is undefined

I was following a tutorial & completed the tutorial & made the project but after sometime my JS shows a error Uncaught TypeError: totype[totypeIndex] is undefined when i tried to log the type of totype[totypeIndex] firstly it showed String and then after some time it showed undefined.
My Code:
const typedSpan = document.querySelector(".typed") // Storing the Span of HTML of Class 'typed' for changing the text
const totype = ["Fun", "Hard", "Love"] // Array in which all of the words which have to be typed are stored
const delayTyping_char = 200; // 200ms will be waited before next Character of the Text is typed
const delayErasing_text = 150; // 100ms will be waited before removing the next character
const delayTyping_text = 3000; // 2500ms will be waited before everything is erased & next text is typed
let totypeIndex = 0; // Index of The text which is being typed
let charIndex = 0; // Index of The Character which is being typed
function typeText() {
if (charIndex < totype[totypeIndex].length) {
typedSpan.textContent += totype[totypeIndex].charAt(charIndex); // Value of The Span in HTML is = the Character at the index of charIndex of the Text which is being typed
charIndex++; // Adding 1 to charIndex
setTimeout(typeText, delayTyping_char); // Calling typeText Until the charIndex is greater than the lenght of the Text which is being typed
} else {
setTimeout(eraseText, delayTyping_text); // if charIndex is Greater than lenght of the text which is being type then after the time setted (delayTyping_text) erase function will be called
}
}
function eraseText() {
if (charIndex > 0) {
typedSpan.textContent = totype[totypeIndex].substring(0, charIndex - 1); // substring(0, charIndex-1) here charIndex-1 is saying to select the text leaving one of last text behind
charIndex = charIndex - 1; // subtracting 1 from charIndex
setTimeout(eraseText, delayErasing_text); // Will call eraseText() Function until the charIndex is not equal to 0
} else {
totypeIndex++; // If everything is erased then the totypeIndex will be increased and next text in Array (totype) will be typed
if (totypeIndex > totype.length) // If totypeIndex is equal to or greater than the number of text to be typed then
totypeIndex = 0; // totypeIndex will be 0 so that we can start from the first text in the array (toType) and
setTimeout(typeText, delayTyping_text); // after some delay presetted (delayTyping_text) typeText() Function is called
}
}
window.onload = function() {
setTimeout(typeText, delayTyping_text);
} // typText() Function is called when everything is loaded
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #111111;
font-family: monospace;
color: #ffffff;
text-align: center;
}
.wrapper {
height: 100vh;
width: auto;
display: flex;
justify-content: center;
align-items: center;
}
.effect-wrapper {
text-align: center;
font-weight: normal;
}
.typed {
font-weight: bold;
color: #00bdff;
}
.cursor {
display: inline-block;
background-color: #b0ff95;
animation: blinker 800ms infinite;
}
.cursor.typing-true {
animation: none;
}
#keyframes blinker {
0% {
background-color: #b0ff95;
}
50% {
background-color: transparent;
}
100% {
background-color: #b0ff95;
}
}
<div class="wrapper">
<!-- Wrapper Start -->
<h1 class="effect-wrapper">Coding is <span class="typed"></span> <span class="cursor"> </span></h1>
</div>
<!-- Wrapper End -->
Please Tell me what was the error & how do i fix it?
Thanks in Advance
typo is here
if (totypeIndex > totype.length) // If totypeIndex is equal to or greater than the number of text to be typed then
you have to set this statement to be higher or equal so use >= instead of >
const typedSpan = document.querySelector(".typed") // Storing the Span of HTML of Class 'typed' for changing the text
const totype = ["Fun", "Hard", "Love"] // Array in which all of the words which have to be typed are stored
const delayTyping_char = 200; // 200ms will be waited before next Character of the Text is typed
const delayErasing_text = 150; // 100ms will be waited before removing the next character
const delayTyping_text = 3000; // 2500ms will be waited before everything is erased & next text is typed
let totypeIndex = 0; // Index of The text which is being typed
let charIndex = 0; // Index of The Character which is being typed
function typeText() {
if (charIndex < totype[totypeIndex].length) {
typedSpan.textContent += totype[totypeIndex].charAt(charIndex); // Value of The Span in HTML is = the Character at the index of charIndex of the Text which is being typed
charIndex++; // Adding 1 to charIndex
setTimeout(typeText, delayTyping_char); // Calling typeText Until the charIndex is greater than the lenght of the Text which is being typed
} else {
setTimeout(eraseText, delayTyping_text); // if charIndex is Greater than lenght of the text which is being type then after the time setted (delayTyping_text) erase function will be called
}
}
function eraseText() {
if (charIndex > 0) {
typedSpan.textContent = totype[totypeIndex].substring(0, charIndex - 1); // substring(0, charIndex-1) here charIndex-1 is saying to select the text leaving one of last text behind
charIndex = charIndex - 1; // subtracting 1 from charIndex
setTimeout(eraseText, delayErasing_text); // Will call eraseText() Function until the charIndex is not equal to 0
} else {
totypeIndex++; // If everything is erased then the totypeIndex will be increased and next text in Array (totype) will be typed
if (totypeIndex >= totype.length) // If totypeIndex is equal to or greater than the number of text to be typed then
totypeIndex = 0; // totypeIndex will be 0 so that we can start from the first text in the array (toType) and
setTimeout(typeText, delayTyping_text); // after some delay presetted (delayTyping_text) typeText() Function is called
}
}
window.onload = function() {
setTimeout(typeText, delayTyping_text);
} // typText() Function is called when everything is loaded
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #111111;
font-family: monospace;
color: #ffffff;
text-align: center;
}
.wrapper {
height: 100vh;
width: auto;
display: flex;
justify-content: center;
align-items: center;
}
.effect-wrapper {
text-align: center;
font-weight: normal;
}
.typed {
font-weight: bold;
color: #00bdff;
}
.cursor {
display: inline-block;
background-color: #b0ff95;
animation: blinker 800ms infinite;
}
.cursor.typing-true {
animation: none;
}
#keyframes blinker {
0% {
background-color: #b0ff95;
}
50% {
background-color: transparent;
}
100% {
background-color: #b0ff95;
}
}
<div class="wrapper">
<!-- Wrapper Start -->
<h1 class="effect-wrapper">Coding is <span class="typed"></span> <span class="cursor"> </span></h1>
</div>
<!-- Wrapper End -->

How to achieve this typing & deleting effect?

I’d like to replicate this website’s typing & deleting effect: http://www.cantercompanies.com.
I’ve been trying to figure this out using code blocks, HTML, CSS, and JS. However, I can’t seem to get this to work after hours and days of trying.
It’s obviously on my end, as I am fairly new to coding.
I really want this exact typing & deleting effect with blinking cursor. I of course will be using my own logo and fixed text, but need some guidance and help to replicate Canter's example above in the link provided… :-)
You don't need any library,
HTML
<div class="flex">
<p class="header-sub-title" id="word"></p><p class="header-sub-title blink">|</p>
</div>
CSS
#import 'https://fonts.googleapis.com/css?family=Roboto';
html, body {
background-color: #000;
}
h2, a, p {
color: #fff;
font-family: Roboto;
text-decoration: none;
}
p>a {
color: #fd084a;
}
.blink {
animation: blink 0.5s infinite;
}
#keyframes blink{
to { opacity: .0; }
}
.flex {
display: flex;
}
.header-sub-title {
color: #fff;
font-family: "Courier";
font-size: 20px;
padding: 0.1em;
}
JS
const words = ["CSS3.", "HTML5.", "javascript."];
let i = 0;
let timer;
function typingEffect() {
let word = words[i].split("");
var loopTyping = function() {
if (word.length > 0) {
document.getElementById('word').innerHTML += word.shift();
} else {
deletingEffect();
return false;
};
timer = setTimeout(loopTyping, 500);
};
loopTyping();
};
function deletingEffect() {
let word = words[i].split("");
var loopDeleting = function() {
if (word.length > 0) {
word.pop();
document.getElementById('word').innerHTML = word.join("");
} else {
if (words.length > (i + 1)) {
i++;
} else {
i = 0;
};
typingEffect();
return false;
};
timer = setTimeout(loopDeleting, 200);
};
loopDeleting();
};
typingEffect();
Reference:https://codepen.io/haaswill/pen/VKzXvZ

Add type in animated feature

I wish to create a feature where the letters are typed in between a sentence as it is done in this page .
In the above link there is a sentence inside the banner that says
Create meaningful documents
Create persuasive documents
Create impactful documents
If you notice, the 2nd word is changing while the first and the third word remains the same, can anyone please tell how this animated feature can be achieved
This can be done using javascript. Here is a plugin that i made that types letters using setInterval() function.
Original Demo:
typer = function(e, s, d, t) {
var eI = 0;
var speed = s;
var delay = d;
var eLength = t.length;
var z = 1;
function loop() {
var p = $("<div class='azy-typer-container azy-typer-done'></div>");
var c_t = $("<span class='azy-typer-element'></span>")
var c_b = $("<span class='azy-typer-blinker'>|</span>");
$(".azy-typer-blinker").remove();
p.append(c_t).append(c_b);
$(e).append(p);
interval = setInterval(function() {
c_t.text(t[eI].substring(0, z));
if (z + 1 > t[eI].length) {
clearInterval(interval);
eI = eI + 1;
if (eI + 1 <= t.length) {
z = 0;
setTimeout(loop, d);
}
} else {
z = z + 1;
}
}, s)
}
loop();
}
new typer(".container", 100, 1000, ["Hi there!", "This is a typer demo ", "What do you think about this ?"]);
body {
background: black;
}
span {
font-family: "Courier New";
font-size: 24px;
color: #fff;
font-weight: bold;
}
.azy-typer-blinker {
animation: blink 1s infinite;
}
#keyframes blink {
0% {
color: crimson;
}
50% {
color: transparent;
}
100% {
color: crimson;
}
}
.azy-typer-done {
margin-left: 24px;
}
.azy-typer-done:before {
content: ">>";
color: lightgreen;
font-family: "Courier New";
font-size: 24px;
margin-left: -24px;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="container"></div>
You can do some changes to make this code work as you need
typer = function(e, s, d, t) {
var eI = 0;
var speed = s;
var delay = d;
var eLength = t.length;
var z = 1;
function loop() {
var p = $("<div class='azy-typer-container'></div>");
var c_t = $("<span class='azy-typer-element'></span>")
var c_b = $("<span class='azy-typer-blinker'>|</span>");
$(".azy-typer-blinker").remove();
p.append(c_t).append(c_b);
$(e).append(p);
interval = setInterval(function() {
c_t.text(t[eI].substring(0, z));
if (z + 1 > t[eI].length) {
p.addClass("azy-typer-done");
clearInterval(interval);
eI = eI + 1;
if (eI + 1 <= t.length) {
z = 0;
setTimeout(loop, d);
} else {
eI = 0;
z = 0;
setTimeout(loop, d);
}
} else {
z = z + 1;
}
}, s)
}
loop();
}
new typer(".container", 100, 1000, ["Hi there!", "This is a typer demo ", "What do you think about this ?"]);
body {
background: black;
}
span {
font-family: "Courier New";
font-size: 24px;
color: #fff;
font-weight: bold;
}
.azy-typer-blinker {
color: maroon;
}
.azy-typer-container {
display: inline;
}
.azy-typer-done {
display: none;
}
.static {
color: lime;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="container">
<span class="static">Static </span>
</div>
The function takes four arguments
The element to which the text is to be appended
The typing speed
The delay for the next item to appear
The text (should be an array)

Having issues pausing the setInterval and then continuing

I have run into a little issue with a timer clock I was building. All went well but in short this is what it does:
user gets to set the time he/she wants to study or do any activity for
user presses start
the start button changes to a "stop" button
the timer counts down from the time chosen by the user
once the timer hits 0 the clock with stop and button changes back to "start" and everything is reset to 0:00
Note
Once the user taps the start button or the start button the device will vibrate if capable to notify the user.
The Issue
The problem I have is that when the clock is running and the user presses the "Stop Study" button, then the clock stops, yes thats good but when he/she presses the button again (which now should be a "start" button because its essentially paused), then the clock takes the time that was given and starts the clock over from that time the user gave and not continue until 0:00.
I have check out a few articles and I have used variables and switched between the Boolean state and checked if the clock is running or not.
isRunning = !isRunning
I have seen a few say that I should use:
clearInterval(name);
This doesn't work because I dont want to clear the state of the clock or maybe I am doing it wrong.
Code
The link to a fiddle can be found here: https://jsfiddle.net/ToreanJoel/c75vLf8b/
HTML
<br/>
<div class="timer" id="startingTimer">
<p class="title" id="state">Break</p>
<p id="time">00:00</p><span ng-style="{'height':fillHeight, 'background':fillColor }" class="fill" style="height: 0.933333%; background: rgb(153, 204, 0);"></span>
</div>
<br/>
<div class="session" id="toggleSessionStart">
<div class="timer control startingTimercontroller" id="startingTimercontroller">
<p class="title controlTitle" id="StartTimer">Start Study</p>
</div>
<!--<div class="timer control startingPauseTimercontroller" id="startingPauseTimercontroller">
<p class="title controlTitle" id="StartPauseTimer">Start Break</p>
</div>--></div>
<br/>
<header>
<div class="session">
<div class="sessionCtrl">
<p>session length</p>
<input type="number" class="time" id="valueTimerSession" value="10">
</div>
<!--<div class="breakCtrl">
<p>break length</p>
<input type="number" class="time" id="valueTimerBreak" value="5">
</div>--></div>
</header>
CSS
body {
background: #333333;
color: #fff;
}
#time {
font-size: 90px;
position: relative;
top: -40px;
}
#media (max-width: 500px) {
#time {
font-size: 90px;
position: relative;
top: -80px;
}
}
.plus {
background-color: #333333;
color: #fff;
border: none;
cursor: pointer;
font-size: 2em;
outline: none;
}
.time {
font-size: 2.5em;
padding-left: 10px;
padding-right: 10px;
width: 100%;
}
.minus {
background-color: #333333;
color: #fff;
border: none;
cursor: pointer;
font-size: 2em;
outline: none;
}
header {
display: flex;
justify-content: center;
text-align: center;
margin: 0 auto;
color: #fff;
text-transform: uppercase;
padding: 20px;
}
.session .breakCtrl, .session .sessionCtrl {
display: inline;
padding-left: 30px;
padding-right: 30px;
}
.session {
font-size: .8em;
display: flex;
}
.timer {
margin: 0 auto;
text-align: center;
width: 300px;
height: 300px;
font-size: 4em;
border: 2px solid #99CC00;
border-radius: 50%;
cursor: pointer;
position: relative;
z-index: 20;
overflow: hidden;
}
.control {
margin: 0 auto;
text-align: center;
width: 120px;
height: 120px;
font-size: 4em;
border: 2px solid #99CC00;
border-radius: 50%;
cursor: pointer;
position: relative;
z-index: 20;
overflow: hidden;
font-family: sans-serif;
}
.startingTimercontroller {
background: #37B703 !important;
border: 2px solid #fff;
}
.startingPauseTimercontroller {
background: #B70000 !important;
border: 2px solid #fff;
}
.title {
margin: 45px;
margin-bottom: -30px;
}
.controlTitle {
font-size: 28px;
position: relative;
top: 25px;
margin: 0;
}
.heading {
text-align: center;
font-size: 50px;
text-transform: uppercase;
font-family: sans-serif;
}
JS
//event Listener
var clickStart = document.getElementById("toggleSessionStart");
//pauseing the clock
var clockRunning = false;
var clicked = false;
//getting the user value ammount to study and break for
var valueTimerSession = parseInt(document.getElementById('valueTimerSession').value);
function pomodoro(studyTime) {
this.studyTime = studyTime;
this.seconds = 59;
this.timerDOM = document.getElementById("time");
this.state = document.getElementById("state");
this.toggleSessionStart = document.getElementById('toggleSessionStart');
}
pomodoro.prototype.startStudyTicker = function () {
var thisStudyTicker = this;
var seconds = this.seconds - 1;
var DOM = this.timerDOM;
var minutes = this.studyTime - 1;
var loopingSeconds = seconds;
var state = this.state;
var toggleSessionStart = this.toggleSessionStart;
if (clicked && clockRunning) {
console.log('We are runnung');
window.ticker = setInterval(function () {
//save the minutes to global variable
window.minSaved = minutes;
window.secSaved = loopingSeconds;
console.log("The time saved is " + window.minSaved + ":" + window.secSaved);
console.log(minutes + ":" + loopingSeconds);
var tick = loopingSeconds--;
if (loopingSeconds >= 0) {
tick;
DOM.innerHTML = minutes.toString() + ":" + (loopingSeconds < 10 ? '0' + loopingSeconds.toString() : loopingSeconds.toString());
} else {
if (minutes > 0) {
minutes--;
loopingSeconds = seconds;
tick;
DOM.innerHTML = minutes.toString() + ":" + (loopingSeconds < 10 ? '0' + loopingSeconds.toString() : loopingSeconds.toString());
}
if (minutes <= 0) {
//vibrate - Timer is Done
window.navigator.vibrate(300);
console.log('im finished');
clearInterval(ticker);
}
}
}, 1000);
} else {
if (!clicked && !clockRunning) {
clearInterval(ticker);
}
}
}
pomodoro.prototype.stopStudyTicker = function () {
var thisStudyTickerStop = this;
console.log('We are paused');
clearInterval(ticker);
thisStudyTickerStop.startStudyTicker();
}
//get the session title
var sessionTitle = document.getElementById('state');
//the DOM toggle
function toggleDOM(chosenTime) {
if (clicked && clockRunning) {
//started the session - the Title
sessionTitle.innerHTML = "Session";
clickStart.innerHTML =
'<div class="timer control startingPauseTimercontroller" id="startingPauseTimercontroller"><p class="title controlTitle" id="StartTimer">Stop Study</p></div>';
//vibrate
window.navigator.vibrate(300);
//prototype execution
var startStudy = new pomodoro(chosenTime);
startStudy.startStudyTicker();
} else {
sessionTitle.innerHTML = "Break";
clickStart.innerHTML =
'<div class="timer control startingTimercontroller" id="startingTimercontroller"><p class="title controlTitle" id="StartTimer">Start Study</p></div>';
//vibrate
window.navigator.vibrate([100, 100, 100]);
//prototype execution
var stopStudy = new pomodoro();
stopStudy.stopStudyTicker();
}
}
clickStart.addEventListener('click', function () {
//user clicked and the clock starts
clicked = !clicked;
clockRunning = !clockRunning;
//valueTimerBreak = parseInt(document.getElementById('valueTimerBreak').value);
valueTimerSession = parseInt(document.getElementById('valueTimerSession').value);
//the Toggle
toggleDOM(valueTimerSession);
});
I was looking at a few things on stack overflow but nothing really seemed to help as im not trying to use multiple buttons to pause or play but use one that toggles its states and the markup and the layout can be seen on jsFiddle (https://jsfiddle.net/ToreanJoel/c75vLf8b/).
I'm using Prototypal Pattern and I'm not use to it yet but I will be going over everything again just to refactor the code anyway to get use to it.
Thanks in advance
I didn't really understood your code but I made my own, basically if you click on a button and the seconds aren't stored in a variable - store them, else just continue looping. I think you'll understand my code, just replace your javascript with my.
var clickStart = document.getElementById("toggleSessionStart");
var pomodoro = function() {
this.inProgress = false;
this.studyTime = null;
this.timerInstance = null;
this.timerDOM = document.getElementById("time");
this.stateElement = document.getElementById("state");
this.toggleSessionStart = document.getElementById('toggleSessionStart');
}
pomodoro.prototype = {
start: function() {
var parent = this;
if(this.studyTime === null) this.studyTime = parseInt(document.getElementById('valueTimerSession').value, 10) * 60;
this.timerInstance = setInterval(function() {
parent.studyTime--;
if(parent.studyTime < 1) parent.destroy();
else parent.updateTime();
}, 1000);
return this;
},
pause: function() {
clearInterval(this.timerInstance);
this.timerInstance = null;
return this;
},
destroy: function() {
this.pause();
this.studyTime = null;
this.toogleState(false);
this.timerDOM.innerHTML = '00:00';
return this;
},
updateTime: function() {
var totalSec = this.studyTime,
minutes = Math.floor(totalSec / 60),
seconds = totalSec % 60;
this.timerDOM.innerHTML = (minutes < 10 ? "0" + minutes : minutes) + ":" + (seconds < 10 ? "0" + seconds : seconds);
return this;
},
toogleState: function(state) {
this.inProgress = (typeof state !== 'undefined') ? state : !this.inProgress;
if(this.inProgress) {
this.stateElement.innerHTML = "Session";
clickStart.innerHTML = '<div class="timer control startingPauseTimercontroller" id="startingPauseTimercontroller"><p class="title controlTitle" id="StartTimer">Stop Study</p></div>';
this.start();
}
else {
this.stateElement.innerHTML = "Break";
clickStart.innerHTML = '<div class="timer control startingTimercontroller" id="startingTimercontroller"><p class="title controlTitle" id="StartTimer">Start Study</p></div>';
this.pause();
}
window.navigator.vibrate(300);
return this;
}
};
var pomodoroInstance = new pomodoro();
clickStart.addEventListener('click', function () {
pomodoroInstance.toogleState();
});
BTW. there is one problem, you can't stop the timer manually so if user wants to set different time he will have to reload the page. You can add a little button which will trigger destroy() method.
I'd rather implement the logic for the clock in a separate class, after all a clock doesn't need anything but a time, our api will consist on methods to start/stop/pause the timer
The gui then creates a clock instance, whenever you click the start/stop button we just need to call the magic clock methods that control the timer, note that the clock doesn't have a method to render itself, it'd be better to have another class like ClockGUI that has an inner instance of Clock, this new class would just call methods of the Clock instance to update the timer and also update the gui
function Clock (time) {
this.timeLeft = time
this.paused = false
}
Clock.prototype.start = function () {
this.raf = requestAnimationFrame(
this._loop.bind(this)
)
}
Clock.prototype.stop = function () {
cancelRequestAnimationFrame(this.raf)
}
Clock.prototype.togglePause = function () {
this.paused = !this.paused
}
Clock.prototype._update = function (t) {
if (!this.paused) {
this.timeLeft -= t
if (this.timeLeft <= 0) {
this.stop()
}
}
}
Clock.prototype._loop = function () {
this.raf = requestAnimationFrame(this._loop.bind(this))
var now = Date.now()
var delta = now - (this.prev || now)
this._update(delta)
this.prev = now
}
// game
var timeLeft = document.querySelector('#time-left')
var input = document.querySelector('input')
var button = document.querySelector('button')
var started = false
var clock
button.addEventListener('click', function () {
button.innerText = button.innerText === 'start' ? 'pause' : 'start'
if (!started) {
started = true
clock = new Clock(input.value * 1000 * 60)
clock.start()
input.disabled = true
return
}
// toggle the state of the clock
clock.togglePause()
})
function render () {
requestAnimationFrame(render)
// render only if a clock was created
if (clock) {
var time = Math.floor(clock.timeLeft / 1000)
var minutes = Math.floor(time / 60)
var seconds = time % 60
var ms = clock.timeLeft % 1000
timeLeft.innerHTML = minutes + ':' + seconds + ':' + ms
}
}
requestAnimationFrame(render)
<div>
Time left: <span id="time-left"></span>
</div>
<button> start </button>
<input type="number" value="10">
As you've seen the clock is not controlled by setInterval but by requestAnimationFrame, the problem of having a fixed 1000 ms interval is the pause behavior you want:
pause: just call clearInterval
start: compute how much time is left that isn't part of a second e.g. timeLeft % 1000, set a timeout on that much time left and then call set interval again
You could use setInterval with a small frequency like 10ms, however it's not guaranteed that the function will be called with exactly 10ms but as close as 10ms therefore you still need to compute the time elapsed between two calls of the setInterval function, in the example above this is done on Clock.prototype._loop

Categories