I have a simple script designed to increment a counter each time an image is clicked:
<script>
function clickCounter() {
if (typeof(Storage) !== "undefined") {
if (localStorage.clickcount) {
localStorage.clickcount = Number(localStorage.clickcount) + 1;
} else {
localStorage.clickcount = 1;
}
document.getElementById("counter").innerHTML = localStorage.clickcount;
} else {
document.getElementById("counter").innerHTML = "Your browser does not support web storage...";
}
}
</script>
The html:
<img id="heart" src="../assets/images/redheart.png" title="Give Love!" onclick="clickCounter()"><p id="counter"></p>
It's working fine but I'd like to be able to display the count BEFORE the click. Right now the counter shows only once the image button is clicked.
Thanks for any and all input.
If this isn't what you're going for, please let me know.
First, a few tweaks:
For accessibility reasons:
I made the <img> a <button> (with a background image). Buttons are super accessible and keyboard friendly (tab, enter key, etc.)
Added an aria-label to the button, since it contains no text content
Cleaned up the JavaScript a little.
Behavior:
On first page load…
Then we perform five clicks followed by a page refresh.
Note: This demo will not work here on Stack Overflow due to security restrictions in the editor. Have a look over in the fiddle I've created.
document.querySelector("button").addEventListener("click", clickCounter);
document.addEventListener("DOMContentLoaded", showValue);
let counter = document.getElementById("counter");
function showValue() {
counter.innerHTML = `Current count = ${localStorage.clickcount || 0}`;
}
function clickCounter() {
if (typeof(Storage) !== "undefined") {
if (localStorage.clickcount) {
localStorage.clickcount = Number(localStorage.clickcount) + 1;
} else {
localStorage.clickcount = 1;
}
showValue();
} else {
counter.innerHTML = "Your browser does not support web storage...";
}
}
button {
background-image: url(http://icons.iconarchive.com/icons/benzlee/free-christmas/512/heart-icon.png);
background-repeat: no-repeat;
background-size: 64px 64px;
background-color: transparent;
border: none;
display: inline-block;
width: 64px;
height: 64px;
cursor: pointer;
}
<button title="Give Love!" aria-label="Give Love!">
<div id="counter"></div>
https://jsfiddle.net/mgejhcnf/3/
Related
I am absolutely a noob when it comes to Javascript so I hope someone can help me please. I made a very simple Vanilla JS + HTML code that counts the number of times that it reaches 10 seconds (10 seconds = 1 count). This code will also refresh the page onmouseleave and when I change tab using window.onblur. My problem is that every time the page refreshes, the counter will go back to zero. What I want is that for the counter to deduct just one (or a specific number of) count every page refresh instead of completely restarting the count to zero. Please help me with Vanilla Javascript only and no JQuery (because I am planning to use this code personally and offline). Thank you in advance.
For those who may wonder what's this code is for, I want to create this to encourage myself to stay away from my computer for a certain period everyday. Like, if I can stay away from my computer for 100 counts, then I can use my computer freely after. I am addicted to the internet and I want to make this as my own personal way of building self-control.
Here is my code:
<style>
label {
color: orange;
}
p {
border-radius: 0px;
color: black;
text-decoration: none;
font-family: Consolas !important;
font-size: 16px;
font-weight: normal;
outline: none;
line-height: 0.25 ;
}
</style>
<body onmouseleave="window.location.reload(true)">
<p>You have earned <label id="pointscounter">00</label> point/s.</p>
<script>
var PointsLabel = document.getElementById("pointscounter");
var totalCountPoints = 0;
setInterval(setTimePoints, 10000);
function setTimePoints() {
++totalCountPoints;
PointsLabel.innerHTML = pad(totalCountPoints);
}
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
</script>
<script>
var blurred = false;
window.onblur = function() { blurred = true; };
window.onfocus = function() { blurred && (location.reload()); };
</script>
</body>
Storage
If you want the data to survive a reload, you need to save it somewhere. There are multiple options you can use. I used localStorage. You can learn more about it here: https://www.w3schools.com/jsref/prop_win_localstorage.asp. localStorage even survives closing the Browser Tab.
If you want to reset the data in a new session, you can use sessionStorage (just replace localStorage with sessionStorage): https://www.w3schools.com/jsref/prop_win_sessionstorage.asp.
What I did:
Save data on blur
If a blur-event occurs, the data is saved.
I also stopped the interval because there is no need for the interval anymore.
blurred variable
There is (currently?) no need for this variable.
The only usecase seems to be:
window.onfocus = function() {
blurred && location.reload();
};
To my knowledge you don't need this variable here.
Comming back
If the user already has points in localstorage, the current Points are calculated based on the points in localstorage. It currently deducts 1 point.
Using onmouseleave
I replaced the location.reload(true) on the body-tag with a function call. Everytime the mouse leaves, it calls this function. This function calls the onBlur function. The onBlur function is there, to ensure, that both window.onblur and onmouseleave do the same thing (save & stop). After the onBlur function is called, an EventListener is added to wait for mouseenter. When the mouse is seen again, we can reload the page with the onFocus function. It wouldn't reload the page as soon as the mouse left, because the timer would start (bc of reload), even if the mouse wasn't on the document.
Todo:
There is currently no check to see, if a the mouse in on the document after a reload. The timer will begin, even if the mouse isn't on the document.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
label {
color: orange;
}
p {
border-radius: 0px;
color: black;
text-decoration: none;
font-family: Consolas !important;
font-size: 16px;
font-weight: normal;
outline: none;
line-height: 0.25;
}
</style>
</head>
<body onmouseleave="mouseLeft()">
<p>You have earned <label id="pointscounter">00</label> point/s.</p>
<script>
var PointsLabel = document.getElementById("pointscounter");
var totalCountPoints = 0;
// Calculate Points if user has already collected points
if (localStorage.getItem("points") !== null) {
// You can change, how many points to deduct
const pointsToDeduct = 1;
var tempPoints = localStorage.getItem("points");
totalCountPoints = tempPoints - pointsToDeduct;
// Reset to 0 if points now negative
if (totalCountPoints < 0) {
totalCountPoints = 0;
}
PointsLabel.innerHTML = pad(totalCountPoints);
}
// need to save, to stop/clear it later
var timePointsInterval = setInterval(setTimePoints, 10000);
function setTimePoints() {
++totalCountPoints;
PointsLabel.innerHTML = pad(totalCountPoints);
}
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
function mouseLeft() {
onBlur();
document.addEventListener("mouseenter", onFocus);
}
function onBlur() {
// save Current Points:
localStorage.setItem("points", totalCountPoints);
//stop the timer
clearInterval(timePointsInterval);
}
function onFocus() {
location.reload();
}
// Blur Detection
var blurred = false;
window.onblur = function () {
// [-] blurred = true;
onBlur();
};
window.onfocus = function () {
// [-] blurred && location.reload();
onFocus();
};
</script>
</body>
</html>
I'm quite new to JS.
I want to have my html page stay the same when JS text will be appearing in one exact place without starting from blank page.
I trigger JS function via button on HTML, function in HTML:
function match () {
setTimeout(function () {
player_hp -= monster_dmg
monster_hp -= player_dmg
if (player_hp<=0) {
document.write("\nPlayer dies!")
menu();
return;
}
if (monster_hp<=0) {
document.write("\nPlayer wins!")
menu();
return;
}
if (fight=1) {
document.write("\nPlayer hp:" + player_hp)
document.write("\nMonster hp:" + monster_hp)
document.write("\n");
match()
}
}, interval)
}
One easy way to handle this is to simply create a <div> or a <span> element that has an ID attribute like this:
<div id="status"> </div>
Now you can access this element by using the Javascript method
document.querySelector("#status") and then use the innerHTML function of that element to change the internal content. You can even place the document.querySelector function into a convenient function which I have named send_status()
Here's the whole thing
/* default values */
var player_hp = 300;
var monster_dmg = 30;
var monster_hp = 200;
var interval = 500;
var player_dmg = 50;
match();
/* heres a function that will replace your document.write() functions */
function send_status(message) {
document.querySelector("#status").innerHTML = message;
}
function match() {
setTimeout(function() {
player_hp -= monster_dmg
monster_hp -= player_dmg
if (player_hp <= 0) {
send_status("\nPlayer dies!") // replaced document.write with send_status
menu();
return;
}
if (monster_hp <= 0) {
send_status("\nPlayer wins!")
menu();
return;
}
if (fight = 1) {
send_status("\nPlayer hp:" + player_hp)
send_status("\nMonster hp:" + monster_hp)
send_status("\n");
match()
}
}, interval)
}
function menu() {}
#game {
width: 100%;
height: 100px;
border: 2px solid black;
}
#status {
width: 100%;
height: 50px;
background-color: grey;
}
<div id="game">Game Goes Here</div>
<!-- here is your status area -->
<div id="status"></div>
You should create a results div, in which will be shown the match result.
Just add <div id="match_results"></div> in your HTML code.
And replace all yours document.write() for
document.getElementById('match_results').innerHTML += "<br>Player wins!"
This command is appending content in the element with ID match_results.
You should use <br> instead of \n because it is the proper way to break line in HTML code.
EDIT: 'mouseleave' event is constantly being triggered, although the mouse does not leave the element.
Code works as intended in: chrome, mozilla, edge, opera. But not safari!
I have a vanilla JavaScript solution that changes images every 1000ms when mouse hovered on parent element. There can be any amount of images inside wrapper and this should still work. To be more clear, javascript adds "hidden" class for every image and removes it from the one who's turn is to be displayed. (Code is in fiddle).
In safari it seems to be stuck swapping 2-3rd image. Am I using wrong dom-manipulation approach? How can I find the error?
Problem presentation: https://jsfiddle.net/pcwudrmc/65236/
let imageInt = 0;
let timeOut;
let imagesWrapper = document.querySelectorAll('.items-box__item');
// Events for when mouse enters/leaves
imagesWrapper.forEach(el => {
el.addEventListener('mouseenter', () => startAnim(el));
el.addEventListener('mouseleave', () => stopanim(el));
});
// DOM Manipulation functions
function changeImages(el) {
imageInt += 1;
if (imageInt === el.children[0].children.length) {
// reset to 0 after going through all images
imageInt = 0;
}
for (let i = 0; i < el.children[0].children.length; i++) {
// Adds "hidden" class to ALL of the images for a product
el.children[0].children[i].classList.add('hidden');
}
// Removes "hidden" class for one
el.children[0].children[imageInt].classList.remove('hidden');
// changeImage calls itself again after 1 second, if hovered
timeOut = setTimeout(changeImages.bind(null, el), 1000);
}
function changeBack(el) {
for (let i = 0; i < el.children[0].children.length; i++) {
// Adds "hidden" class to ALL of the images for a product
el.children[0].children[i].classList.add('hidden');
}
// Removes "hidden" class for the first image of the item
el.children[0].children[0].classList.remove('hidden');
}
startAnim = element => { changeImages(element) }
stopanim = element => {
changeBack(element);
clearTimeout(timeOut);
imageInt = 0;
}
.items-box__item {
width: 300px;
height: 300px;
}
.items-box__item--main-image {
object-fit: contain;
width: 90%;
height: 265px;
}
.hidden {
display: none;
}
<h3>Hover on pic and hold mouse</h3>
<div class="items-box__item">
<a href="/">
<img class="items-box__item--main-image" src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948251/yrllszgndxzlydbycewc.jpg"/>
<img class="items-box__item--main-image hidden" src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948251/e96i5zbvxxuxsdczbh9d.jpg"/>
<img class="items-box__item--main-image hidden" src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948252/boaqfs3yuc4r7mvhsqqu.jpg"/>
</a>
</div>
You need to look at relatedTarget of mouseleave event, as both mouseenter and mouseleave happen every time the displayed image changes.
Also your code might be simplified. See the snippet below. Hope it helps.
const play = (box) => {
while (!box.classList.contains('items-box__item')) box = box.parentElement;
var img = box.querySelector('.show');
img.classList.remove('show');
(img.nextElementSibling || box.firstElementChild).classList.add('show');
}
const stop = ({target: box, relatedTarget: rt}) => {
while (!box.classList.contains('items-box__item')) box = box.parentElement;
while (rt != box && rt) rt = rt.parentElement;
if (rt === box) return;
box.querySelector('.show').classList.remove('show');
box.firstElementChild.classList.add('show');
box.play = clearInterval(box.play);
}
[...document.querySelectorAll('.items-box__item')]
.forEach((box) => {
box.addEventListener(
'mouseenter',
function() {
if (box.play) return;
play(box);
box.play = setInterval(() => play(box), 1000);
}
);
box.addEventListener('mouseleave', stop);
});
.items-box__item {
display: block;
width: 300px;
height: 300px;
}
.items-box__item img {
object-fit: contain;
width: 90%;
height: 265px;
display: none;
}
img.show {
display: initial
}
<h3>Hover on pic and hold mouse</h3>
<a class="items-box__item" href="/">
<img class="show" src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948251/yrllszgndxzlydbycewc.jpg">
<img src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948251/e96i5zbvxxuxsdczbh9d.jpg">
<img src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948252/boaqfs3yuc4r7mvhsqqu.jpg">
</a>
I'm making a function that displays a modal, and the modal has two buttons. I want this function to wait until one of the two buttons has been clicked, and return a value that corresponds to which button is clicked.
Here's a sample code that I came up with:
function myFunc()
{
var val=0;
buttonA = document.getElementById('buttonA');
buttonB = document.getElementById('buttonB');
buttonA.onclick = function(){
//do something
val = 1;
}
buttonB.onclick = function(){
//do something
val = 2;
}
while(val == 0);
return val;
}
The problem in this code is that the page becomes unresponsive because of the infinite loop, hence it isn't possible to change the value of val once initialised.
To be more precise, I want the main thread (on which myFunc is being implemented) to sleep until one of the other two threads (each of buttonA and buttonB) is clicked.
Is there some other work-around for this ? Please answer in Javascript only (no jQuery). Thanks.
Try something more like this:
function myFunc()
{
buttonA = document.getElementById('buttonA');
buttonB = document.getElementById('buttonB');
buttonA.onclick = function(){
//do something
differentFunc(1)
}
buttonB.onclick = function(){
//do something
differentFunc(2)
}
}
This is a different way to make the function more versatile (edited per your comment):
function myFunc(callback)
{
buttonA = document.getElementById('buttonA');
buttonB = document.getElementById('buttonB');
buttonA.onclick = function(){
//do something
callback(1)
}
buttonB.onclick = function(){
//do something
callback(2)
}
}
and call it like
myFunc(function(result) {
// do stuff with result
}
Javascript is naturally single-threaded. Any code that waits infinitely like that will cause a hangup and disallow input. There are ways to write async functions, namely using Promises like I did for a minute there, but it's generally easier to make your code work synchronously.
If I understand the OP's purpose is to create a modal with 2 choices like a confirm()? But for some reason confirm() isn't suitable? So a value on each button and it waits for user interaction? Unless I'm missing something fairly important, I have made a dynamically generated modal (no manual markup) that has 2 buttons. The purpose and result elude me so I left it with one event listener and a function with a simple ternary condition to which the alerts can be replaced by appropriate statements or expression at OP's discretion.
SNIPPET
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
.modal {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
background:transparent;
}
.ui {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: table-cell;
border: 3px ridge grey;
border-radius: 6px;
}
button {
font-size: 24px;
}
</style>
</head>
<body>
<script>
var frag = document.createDocumentFragment();
var modal = document.createElement('div');
var ui = document.createElement('div');
var on = document.createElement('button');
var off = document.createElement('button');
modal.className = 'modal';
ui.className = 'ui';
on.id = 'on';
on.textContent = 'On';
off.id = 'off';
off.textContent = 'Off';
frag.appendChild(modal);
modal.appendChild(ui);
ui.appendChild(on);
ui.appendChild(off);
ui.addEventListener('click', status, false);
function status(e) {
var tgt = e.target.id;
tgt === 'on' ? alert('ON!') : alert('OFF!');
}
document.body.appendChild(frag);
</script>
</body>
</html>
I am trying to make a basic theme switching code using HTML and JavaScript. But if I hit back or refresh, the selection is cancelled and the page is back to normal.
Can I add any function in JavaScript so that the selection stays till cache clean or something like that?
var x = 0;
var themeValue = 0;
function changeTheme3() {
themeValue ++;
if (themeValue > 2) {
themeValue = 1;
}
if (themeValue == 1) {
document.body.style.backgroundColor = "grey";
} else if (themeValue == 2) {
document.body.style.backgroundColor = "white";
}
}
button {
display: block;
margin: 0 auto;
padding: 0;
width: 250px;
height: 70px;
border: 2px solid #1ECD97;
border-radius: 40px;
background: transparent;
color: #1ECD97;
letter-spacing: 1px;
font-size: 18px;
font-family: 'Montserrat', sans-serif;
}
button:hover {
background-color: #1ECD97;
color: #fff;
}
<div id="theme-button">
<button type="button" onclick="changeTheme3()">Change Theme</button>
</div>
I'd suggest making use of LocalStorage. If you want the user to be able to customize their experience, you need to save that somewhere. This is typically done through a Session or Database, however both of those are back-end solutions.
For front-end, you have the options of setting a cookie or using localstorage. If you want the theme selection to be limited to a single session, you can also use sessionStorage
For implementation
function changeTheme3() {
themeValue ++;
if (themeValue > 2) {
themeValue = 1;
localStorage.setItem('themeValue', themeValue);
}
if (themeValue == 1) {
document.body.style.backgroundColor = "grey";
} else if (themeValue == 2) {
document.body.style.backgroundColor = "white";
}
}
Then, somewhere when you load a page, you need to pull this value from storage. (Note, localStorage saves values as strings, so you may want to parseInt() your value when retrieving).
function loadTheme(){
if (localStorage && localStorage.getItem('themeValue'){
var storedTheme = parseInt(localStorage.getItem('themeValue'));
//do your logic to set the theme based on value
} else {
//do nothing or choose to set your default theme as the value into storage
}
}
One side note, depending on which browsers you are supporting, you may want to make sure you're checking to see if localStorage is supported. It has good support for any modern browser.
you can do that using sqlite html5 for example check this link http://www.html5rocks.com/en/tutorials/webdatabase/todo/ :D
here for simple example sir
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
var db = openDatabase('db', '1.0', 'theme selector', 1000); //db name, version, desc, size in byte
var msg;
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS THEME (id unique, theme)');
});
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM THEME WHERE id = 1', [], function (tx, results) {
var len = results.rows.length, i;
var value = 'theme_1';
if(len == 0 ){
tx.executeSql('INSERT INTO THEME (id, theme) VALUES (1, ?)', [value]);
}else{
value = results.rows.item(0).theme;
}
document.querySelector('#theme_selected').innerHTML = value;
document.getElementById('theme_select').value = value;
}, null);
});
function update (){
var selector = document.getElementById('theme_select');
var value = selector[selector.selectedIndex].value;
db.transaction(function (tx) {
tx.executeSql('UPDATE THEME SET theme = ? WHERE id=1', [value]);
document.querySelector('#theme_selected').innerHTML = value;
});
}
</script>
</head>
<body>
<select id="theme_select">
<option value="theme_1" >theme 1</option>
<option value="theme_2">theme 2</option>
</select>
<button onclick="update()">update</button>
<div id="theme_selected"></div>
</body>
</html>