I have an application that I have written that adds a set of traffic lights on the screen upon the press of a button. The traffic light automatically cycles from red to yellow after 10 seconds, then on to green after two seconds, then back to yellow after 10 seconds and finally back to red. I would like to add several of these traffic lights to the screen and have them operating at the same time, but when I add a second traffic light, the first traffic light's sequence is frozen at whatever state it has reached. Any ideas as to how to get it to continue to operate - and thus have more than one light cycling through the sequence at the same time?
The transition is controlled by javascript via a state machine.
Anyone have any ideas as to how to get this working smoothly?
Thanks!
Rob
London, UK
var i = 1;
var TrafficLight = function(i) {
var count = 0;
var light_container = document.getElementById('light-container-' + i);
var currentState = new Red(this, light_container);
this.change = function(state) {
currentState = state;
currentState.go();
}
this.start = function() {
currentState.go();
}
}
var Red = function(light, light_container) {
this.light = light;
this.go = function() {
light_container.querySelector('#inner-circle-red').style.backgroundColor = '#d8412c';
console.log(light_container);
setTimeout(function() {
light.change(new Yellow(light, 'red', light_container))
}, 10000);
}
}
var Yellow = function(light, origin, light_container) {
this.light = light;
this.go = function() {
light_container.querySelector('#inner-circle-yellow').style.backgroundColor = '#fad201';
setTimeout(function() {
if (origin == 'red') {
light.change(new Green(light, light_container));
light_container.querySelector('#inner-circle-red').style.backgroundColor = '#111111';
light_container.querySelector('#inner-circle-yellow').style.backgroundColor = '#111111';
} else if (origin == 'green') {
light.change(new Red(light, light_container));
light_container.querySelector('#inner-circle-yellow').style.backgroundColor = '#111111';
}
}, 2000);
}
}
var Green = function(light, light_container) {
this.light = light;
this.go = function() {
light_container.querySelector('#inner-circle-green').style.backgroundColor = '#33A532';
setTimeout(function() {
light.change(new Yellow(light, 'green', light_container))
light_container.querySelector('#inner-circle-green').style.backgroundColor = '#111111';
}, 10000);
}
};
function run() {
document.getElementById("container").innerHTML += '<div id="light-container-' + i + '"><div class="outer-circle-red"><div id="inner-circle-red"></div></div><div class="outer-circle-yellow"><div id="inner-circle-yellow"></div></div><div class="outer-circle-green"><div id="inner-circle-green"></div></div></div>';
var light = new TrafficLight(i);
light.start();
i++;
}
.outer-circle-red,
.outer-circle-yellow,
.outer-circle-green {
background-color: #696969;
border: 2px solid black;
width: 50px;
height: 40px;
border-radius: 15px;
display: table;
}
#light-container-1,
#light-container-2,
#light-container-3 {
margin-top: 20px;
float: left;
margin-left: 70px;
}
#inner-circle-red,
#inner-circle-yellow,
#inner-circle-green {
width: 20px;
height: 20px;
border-radius: 25px;
border: 2px solid #111111;
margin: 0 auto;
margin-top: 7.5px;
background-color: #111111;
}
#button {
width: 200px;
height: 20px;
padding: 10px;
background-color: blue;
color: #ffffff;
cursor: pointer;
}
<div id="button" onclick="run()">+ Add a new traffic light</div>
<div id="container">
</div>
One issue is, as Lewis already stated that you have multiple elements with the same id. JavaScript can only handle one of those.
The other issue is that you delete the content of #container by using innerHTML. The references you have stored in your TrafficLight, Red, Yellow, and Green classes is then destroyed. The store works correctly, but it can't be seen so. Use the DOM manipulation instead.
var light_container = document.createElement('div');
light_container.id = "light-container-" + i;
light_container.innerHTML = '<div class="outer-circle-red"><div class="inner-circle-red"></div></div><div class="outer-circle-yellow"><div class="inner-circle-yellow"></div></div><div class="outer-circle-green"><div class="inner-circle-green"></div></div>';
document.getElementById("container").appendChild(light_container);
var i = 1;
var TrafficLight = function(i) {
var count = 0;
var light_container = document.getElementById('light-container-' + i);
var currentState = new Red(this, light_container);
this.change = function(state) {
currentState = state;
currentState.go();
}
this.start = function() {
currentState.go();
}
}
var Red = function(light, light_container) {
this.light = light;
this.go = function() {
light_container.querySelector('.inner-circle-red').style.backgroundColor = '#d8412c';
setTimeout(function() {
light.change(new Yellow(light, 'red', light_container))
}, 10000);
}
}
var Yellow = function(light, origin, light_container) {
this.light = light;
this.go = function() {
light_container.querySelector('.inner-circle-yellow').style.backgroundColor = '#fad201';
setTimeout(function() {
if (origin == 'red') {
light.change(new Green(light, light_container));
light_container.querySelector('.inner-circle-red').style.backgroundColor = '#111111';
light_container.querySelector('.inner-circle-yellow').style.backgroundColor = '#111111';
} else if (origin == 'green') {
light.change(new Red(light, light_container));
light_container.querySelector('.inner-circle-yellow').style.backgroundColor = '#111111';
}
}, 2000);
}
}
var Green = function(light, light_container) {
this.light = light;
this.go = function() {
light_container.querySelector('.inner-circle-green').style.backgroundColor = '#33A532';
setTimeout(function() {
light.change(new Yellow(light, 'green', light_container))
light_container.querySelector('.inner-circle-green').style.backgroundColor = '#111111';
}, 10000);
}
};
function run() {
var light_container = document.createElement('div');
light_container.id = "light-container-" + i;
light_container.innerHTML = '<div class="outer-circle-red"><div class="inner-circle-red"></div></div><div class="outer-circle-yellow"><div class="inner-circle-yellow"></div></div><div class="outer-circle-green"><div class="inner-circle-green"></div></div>';
document.getElementById("container").appendChild(light_container);
var light = new TrafficLight(i);
light.start();
i++;
}
.outer-circle-red,
.outer-circle-yellow,
.outer-circle-green {
background-color: #696969;
border: 2px solid black;
width: 50px;
height: 40px;
border-radius: 15px;
display: table;
}
[id^=light-container] {
margin-top: 20px;
float: left;
margin-left: 70px;
}
.inner-circle-red,
.inner-circle-yellow,
.inner-circle-green {
width: 20px;
height: 20px;
border-radius: 25px;
border: 2px solid #111111;
margin: 0 auto;
margin-top: 7.5px;
background-color: #111111;
}
#button {
width: 200px;
height: 20px;
padding: 10px;
background-color: blue;
color: #ffffff;
cursor: pointer;
}
<div id="button" onclick="run()">+ Add a new traffic light</div>
<div id="container">
</div>
I'd imagine it's because you aren't using unique #IDs. Here's an example from your code;
light_container.querySelector('#inner-circle-red').style.backgroundColor
If there's more than one #inner-circle-red on the page, you'll see errors like this. Perhaps try and be more specific with your selectors - like this;
#light-container-1 .inner-circle-red
#light-container-2 .inner-circle-red
I also added a couple of variables and a loop to help manage multiple lights;
for(let i = 1; i <= lights; i++){
var light = new TrafficLight(i);
light.start();
}
See the snippet below for all changes;
var lights = 0;
var TrafficLight = function(i) {
var count = 0;
var light_container = document.getElementById('light-container-' + i);
var currentState = new Red(this, light_container);
this.change = function(state) {
currentState = state;
currentState.go();
}
this.start = function() {
currentState.go();
}
}
var Red = function(light, light_container) {
this.light = light;
this.go = function() {
light_container.querySelector('.inner-circle-red').style.backgroundColor = '#d8412c';
console.log(light_container);
setTimeout(function() {
light.change(new Yellow(light, 'red', light_container))
}, 10000);
}
}
var Yellow = function(light, origin, light_container) {
this.light = light;
this.go = function() {
light_container.querySelector('.inner-circle-yellow').style.backgroundColor = '#fad201';
setTimeout(function() {
if (origin == 'red') {
light.change(new Green(light, light_container));
light_container.querySelector('.inner-circle-red').style.backgroundColor = '#111111';
light_container.querySelector('.inner-circle-yellow').style.backgroundColor = '#111111';
} else if (origin == 'green') {
light.change(new Red(light, light_container));
light_container.querySelector('.inner-circle-yellow').style.backgroundColor = '#111111';
}
}, 2000);
}
}
var Green = function(light, light_container) {
this.light = light;
this.go = function() {
light_container.querySelector('.inner-circle-green').style.backgroundColor = '#33A532';
setTimeout(function() {
light.change(new Yellow(light, 'green', light_container))
light_container.querySelector('.inner-circle-green').style.backgroundColor = '#111111';
}, 10000);
}
};
function run() {
lights++;
document.getElementById("container").innerHTML += '<div id="light-container-' + lights + '"><div class="outer-circle-red"><div class="inner-circle-red"></div></div><div class="outer-circle-yellow"><div class="inner-circle-yellow"></div></div><div class="outer-circle-green"><div class="inner-circle-green"></div></div></div>';
for(let i = 1; i <= lights; i++){
var light = new TrafficLight(i);
light.start();
}
}
.outer-circle-red,
.outer-circle-yellow,
.outer-circle-green {
background-color: #696969;
border: 2px solid black;
width: 50px;
height: 40px;
border-radius: 15px;
display: table;
}
#light-container-1,
#light-container-2,
#light-container-3 {
margin-top: 20px;
float: left;
margin-left: 70px;
}
.inner-circle-red,
.inner-circle-yellow,
.inner-circle-green {
width: 20px;
height: 20px;
border-radius: 25px;
border: 2px solid #111111;
margin: 0 auto;
margin-top: 7.5px;
background-color: #111111;
}
#button {
width: 200px;
height: 20px;
padding: 10px;
background-color: blue;
color: #ffffff;
cursor: pointer;
}
<div id="button" onclick="run()">+ Add a new traffic light</div>
<div id="container">
</div>
Thanks for your answer. Just as an extra feature I have added a control which allows the user to start each traffic light individually. I am trying to figure out how to enable the user to permanently stop each traffic light, without it stopping all the others.
Here is my updated code so far - note the addition of the 'this.stop()' function inside of var Red. I would like the code to stop the rotation there, rather than continue to yellow and green.
Thanks,
Rob
<!DOCTYPE html>
<html>
<head>
<title>Traffic Lights</title>
<style type="text/css">
#button {
width: 200px;
height: 20px;
padding: 10px;
background-color: blue;
color: #ffffff;
cursor: pointer;
}
.button {
width: 15px;
height: 20px;
padding: 10px;
background-color: red;
color: #ffffff;
cursor: pointer;
margin: 20px auto;
text-align: center;
text-transform: uppercase;
font-weight: bold;
}
.outer-circle-red, .outer-circle-yellow, .outer-circle-green {
background-color: #696969;
margin: 0 auto;
border: 2px solid black;
width: 50px;
height: 40px;
border-radius: 15px;
display: table;
}
.light-container {
margin: 20px 30px 0 30px;
margin-top: 20px;
float: left;
}
.inner-circle-red, .inner-circle-yellow, .inner-circle-green {
width: 20px;
height: 20px;
border-radius: 25px;
border: 2px solid #111111;
margin: 0 auto;
margin-top: 7.5px;
background-color: #111111;
}
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
margin-top: 20px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
</style>
</head>
<body>
<div id="button" onclick="initiate()">+ Add a new traffic light</div>
<div id="container">
</div>
<script type="text/javascript">
var i = 1;
var TrafficLight = function (i) {
var count = 0;
var light_container = document.getElementById('light-container-' + i);
var currentState = new Red(this, light_container);
this.change = function (state) {
currentState = state;
currentState.go();
}
this.start = function () {
currentState.go();
}
this.stop = function () {
currentState.stop();
}
}
var Red = function (light, light_container) {
this.light = light;
this.go = function () {
light_container.querySelector('.inner-circle-red').style.backgroundColor = '#d8412c';
console.log(light_container);
setTimeout(function(){
light.change(new Yellow(light, 'red', light_container))
}, 12000);
}
this.stop = function () {
light_container.querySelector('.inner-circle-red').style.backgroundColor = '#111111';
light_container.querySelector('.inner-circle-yellow').style.backgroundColor = '#111111';
light_container.querySelector('.inner-circle-green').style.backgroundColor = '#111111';
// Switch all the lights off.
return;
}
}
var Yellow = function (light, origin, light_container) {
this.light = light;
this.go = function () {
light_container.querySelector('.inner-circle-yellow').style.backgroundColor = '#fad201';
setTimeout(function(){
if(origin == 'red'){
light.change(new Green(light, light_container));
light_container.querySelector('.inner-circle-red').style.backgroundColor = '#111111';
light_container.querySelector('.inner-circle-yellow').style.backgroundColor = '#111111';
} else if(origin == 'green'){
light.change(new Red(light, light_container));
light_container.querySelector('.inner-circle-yellow').style.backgroundColor = '#111111';
}
}, 2000);
}
}
var Green = function (light, light_container) {
this.light = light;
console.log('here');
this.go = function () {
light_container.querySelector('.inner-circle-green').style.backgroundColor = '#33A532';
setTimeout(function(){
light.change(new Yellow(light, 'green', light_container))
light_container.querySelector('.inner-circle-green').style.backgroundColor = '#111111';
}, 14000);
}
};
function initiate(){
var light_container = document.createElement('div');
light_container.id = "light-container-" + i;
light_container.className = "light-container";
light_container.innerHTML = '<div class="outer-circle-red"><div class="inner-circle-red"></div></div><div class="outer-circle-yellow"><div class="inner-circle-yellow"></div></div><div class="outer-circle-green"><div class="inner-circle-green"></div></div><label class="switch"><input type="checkbox" class="off" onclick="toggleRun(this, ' + i + ');"><span class="slider round"></span></label>';
document.getElementById("container").appendChild(light_container);
i++;
}
function toggleRun(item, i){
if(item.className == "off"){
item.className="on";
run(i);
} else {
item.className="off";
stop(i);
}
}
function run(i){
var light = new TrafficLight(i);
light.start();
}
function stop(i){
var light = new TrafficLight(i);
light.stop();
}
function exit( status ){
var i;
if (typeof status === 'string') {
alert(status);
}
window.addEventListener('error', function (e) {e.preventDefault();e.stopPropagation();}, false);
var handlers = [
'copy', 'cut', 'paste',
'beforeunload', 'blur', 'change', 'click', 'contextmenu', 'dblclick', 'focus', 'keydown', 'keypress', 'keyup', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'resize', 'scroll',
'DOMNodeInserted', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument', 'DOMNodeInsertedIntoDocument', 'DOMAttrModified', 'DOMCharacterDataModified', 'DOMElementNameChanged', 'DOMAttributeNameChanged', 'DOMActivate', 'DOMFocusIn', 'DOMFocusOut', 'online', 'offline', 'textInput',
'abort', 'close', 'dragdrop', 'load', 'paint', 'reset', 'select', 'submit', 'unload'
];
function stopPropagation (e) {
e.stopPropagation();
// e.preventDefault(); // Stop for the form controls, etc., too?
}
for (i=0; i < handlers.length; i++) {
window.addEventListener(handlers[i], function (e) {stopPropagation(e);}, true);
}
if (window.stop) {
window.stop();
}
throw '';
}
</script>
</body>
</html>```
Related
I am working on the etch-a-sketch project with The Odin Project and there's one thing I can't figure out. In the original project the grid elements will start to change colors once I hover over them but what I want to do is to only start the hovering function after I press the left mouse button.
Here is a working example of what I want to achieve.
const container = document.getElementById('container');
const reset = document.getElementById('reset');
const eraser = document.querySelector('#eraser');
const pencil = document.querySelector('#pencil');
const rainbow = document.querySelector('#rainbow');
const pickColor = document.querySelector('#color');
const shading = document.querySelector('#shading')
const gridSize = document.querySelector('#range');
let gridValue = document.querySelector('.grid-size');
let mode = '';
gridValue.textContent = `50x50`; // display default size of the grid
// create grid
function createGrid(size) {
size = gridSize.value;
container.style.gridTemplateColumns = `repeat(${size}, 1fr)`;
container.style.gridTemplateRows = `repeat(${size}, 1fr)`;
for (let i = 0; i < size * size; i++) {
const grid = document.createElement('div');
grid.setAttribute('id', 'grid');
container.appendChild(grid);
grid.addEventListener('mouseover', changeColor);
}
}
createGrid();
// change grid size depending on the user's choice
gridSize.addEventListener('input', function (e) {
let currentSize = e.target.value;
gridValue.textContent = `${currentSize}x${currentSize}`;
resetGrid();
});
// change color
function changeColor(e) {
if (mode === 'pencil') {
e.target.classList.add('black');
e.target.classList.remove('white');
e.target.style.backgroundColor = null;
e.target.style.opacity = null;
} else if (mode === 'rainbow') {
const green = Math.floor(Math.random() * 250);
const blue = Math.floor(Math.random() * 250);
const red = Math.floor(Math.random() * 250);
e.target.style.backgroundColor = `rgb(${red}, ${green}, ${blue})`;
e.target.style.opacity = null;
} else if (mode === 'erase') {
e.target.classList.add('white');
e.target.style.backgroundColor = null;
e.target.style.opacity = null;
} else if (mode === 'pickColor') {
e.target.style.backgroundColor = pickColor.value;
e.target.style.opacity = null;
} else if (mode === 'shading') {
e.target.classList.remove('white');
e.target.classList.add('black');
e.target.style.opacity = Number(e.target.style.opacity) + 0.2;
} else {
e.target.classList.add('black')
}
}
// reset grid
reset.addEventListener('click', () => resetGrid());
function resetGrid() {
container.innerHTML = '';
createGrid();
}
// erase
eraser.addEventListener('click', () => eraseMode());
function eraseMode() {
mode = 'erase';
}
// pencil
pencil.addEventListener('click', () => pencilMode());
function pencilMode() {
mode = 'pencil';
}
// rainbow
rainbow.addEventListener('click', () => rainbowMode());
function rainbowMode() {
mode = 'rainbow';
}
// pick a color
pickColor.addEventListener('click', () => pickColorMode());
function pickColorMode() {
mode = 'pickColor';
}
// shading
shading.addEventListener('click', () => shadeMode());
function shadeMode() {
mode = 'shading';
}
#import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght#700;800&display=swap');
body {
text-align: center;
background-color: #4d56bd;
font-family: 'Inconsolata', monospace;
color: white;
}
h1 {
font-weight: 800;
font-size: 40px;
text-shadow: 3px 3px #1d1b1e;
color: #f4ce45
}
#container {
width: 600px;
height: 600px;
border: 3px solid #1d1b1e;
display: grid;
margin: 20px auto;
background-color: white;
box-shadow: 5px 5px #303030;
}
#grid {
border: none;
}
.black {
background-color: black;
}
.white {
background-color: white;
}
.button {
font-family: inherit;
padding: 10px;
font-size: 1em;
background-color: #303030;
box-shadow: 2px 2px #1d1b1e;
color: white;
margin: 0 5px;
}
#reset {
background-color: #f4ce45;
color: black;
}
.button:hover,
#reset:hover {
cursor: pointer;
background-color: #e7455a;
}
.ownColor {
margin: 10px;
}
.pickColor:hover {
cursor: auto;
background-color: #303030;
}
#color {
cursor: pointer;
}
.slider {
-webkit-appearance: none;
width: 30%;
height: 10px;
border-radius: 5px;
background: #1d1b1e;
margin: 10px;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #f4ce45;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: #f4ce45;
cursor: pointer;
}
.grid-size {
font-size: 1.5rem;
margin-top: 20px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
<title>Etch-a-Sketch</title>
</head>
<body>
<h1>ETCH A SKETCH</h1>
<button id="reset" class="button">Reset</button>
<button id="pencil" class="button">Pencil</button>
<button id="eraser" class="button">Eraser</button>
<button id="rainbow" class="button">Rainbow</button>
<button id="shading" class="button">Shading</button>
<div class="ownColor">
<button class="button pickColor">Choose your own color:
<input type="color" id="color"></button>
</div>
<div class="grid-size"></div>
<input type="range" id="range" min="5" max="100" class="slider">
<div id="container"></div>
</body>
<script src="script.js"></script>
</html>
Well basically you can achieve this with a variable isMouseDown.
let isMouseDown;
document.addEventListener('mousedown', () => isMouseDown = true);
document.addEventListener('mouseup', () => isMouseDown = false);
then, use it where you need, for example:
// change color
function changeColor(e) {
if(!isMouseDown) return;
// ... rest of the code
}
var wishlistkey = "wishlist";
// try and fetch an existing wishlist from the localStorage.
var wish_list = localStorage.getItem(wishlistkey);
console.log(wish_list,$.isEmptyObject(wish_list));
if ($.isEmptyObject(wish_list)) { //nothing was saved previously
wish_list = [];
} else {
wish_list = JSON.parse(wish_list);
count_items_in_wishlist_update();
}
$(document).ready(function() {
count_items_in_wishlist_update();
$(".wishlist").on("click", function() {
const office_id = $(this).attr("office_id"),
office_name = $(this).attr("office_name"),
office_str = `<tr class="wishlist-item" id="list_id_${office_id}"><td class="w-pname">${office_name}</td><td class="w-premove" wpid="${office_id}">x</td></tr>`;
//check if the element is in the array
const found = $.inArray(office_id, wish_list) > -1;
if (found) {
$("#list_id_" + office_id).remove();
wish_list = wish_list.filter(id => id != office_id);
show_message(office_name + " Office Removed");
}
else {
$("#wish_list_item").append(office_str);
wish_list.push(office_id);
show_message(office_name + " Office Added");
}
count_items_in_wishlist_update();
});
//adding toggle functionality to the wishlist pannel
$(".wish_list_heading").on("click", function() {
if (!$(this).hasClass("up")) {
$("#wish_list").css("height", "390px");
$(this).addClass("up");
$("#wish_list").css("overflow", "auto");
} else {
$("#wish_list").css("height", "35px");
$(this).removeClass("up");
$("#wish_list").css("overflow", "hidden");
}
});
// Remove function
$("#wish_list_item").on("click", ".w-premove", function() {
office_id = $(this).attr("wpid");
$("#list_id_" + office_id).remove();
wish_list = wish_list.filter(id => id != office_id);
show_message("Office removed");
count_items_in_wishlist_update();
});
});
//Animation
function show_message($msg) {
$("#msg").html($msg);
$top = Math.max(0, (($(window).height() - $("#msg").outerHeight()) / 2) + $(window).scrollTop()) + "px";
$left = Math.max(0, (($(window).width() - $("#msg").outerWidth()) / 2) + $(window).scrollLeft()) + "px";
$("#msg").css("left", $left);
$("#msg").animate({
opacity: 0.6,
top: $top
}, 400, function() {
$(this).css({
'opacity': 1
});
}).show();
setTimeout(function() {
$("#msg").animate({
opacity: 0.6,
top: "0px"
}, 400, function() {
$(this).hide();
});
}, 2000);
}
//Validation against the amount of product being added
function count_items_in_wishlist_update() {
$("#p_label").html(wish_list.length > 0 ? "My Shortlist ("+wish_list.length+")" : "My Shortlist");
$('#wish_list_item').empty();
$(".wishlist").each(function(index, el) {
const office_id = $(el).attr("office_id");
const found = wish_list.includes(office_id);
console.log(office_id,wish_list,found);
const $heart = $(el).find(".fa");
$heart.toggleClass("fa-heart",found);
$heart.toggleClass("fa-heart-o",!found);
if (found) {
const office_name = $(el).attr("office_name"),
office_str = `<tr class="wishlist-item" id="list_id_${office_id}"><td class="w-pname">${office_name}</td><td class="w-premove" wpid="${office_id}">x</td></tr>`;
$('#wish_list_item').append(office_str);
}
});
localStorage.setItem(wishlistkey, JSON.stringify(wish_list));
}
I have made a wishlist where I add items to a wishlist by a button and remove the items from within the wishlist itself by pressing a cross next to the item. However, I need the button to add AND remove items too by having a toggle functionality on the button itself. Also if possible, I would like to have a hyperlink added to the wishlist item. I really haven't got a clue to do that. Can anybody help?
var wishlistkey = "wishlist";
// try and fetch an existing wishlist from the localStorage.
var wish_list = []; // localStorage.getItem(wishlistkey)
if ($.isEmptyObject(wish_list)) { //nothing was saved previously
wish_list = new Array()
} else {
wish_list = JSON.parse(wish_list);
count_items_in_wishlist_update();
$('#wish_list_item').html(wish_list);
}
$(document).ready(function() {
$(".wishlist").on("click", function() {
$data = "";
$office_id = $(this).attr("office_id");
$office_name = $(this).attr("office_name");
$office_str = "<tr class='wishlist-item' id='list_id_" + $office_id + "'><td class='w-pname'>" + $office_name + "</td><td class='w-premove' wpid='" + $office_id + "'>x</td></tr>";
//check if the element is in the array
if ($.inArray($office_id, wish_list) == -1) {
$("#wish_list_item").append($office_str);
wish_list.push($office_str);
// localStorage.setItem(wishlistkey, JSON.stringify(wish_list))
show_message($office_name + " Office Added");
}
count_items_in_wishlist_update();
});
//adding toggle functionality to the wishlist pannel
$(".wish_list_heading").on("click", function() {
if (!$(this).hasClass("up")) {
$("#wish_list").css("height", "390px");
$(this).addClass("up");
$("#wish_list").css("overflow", "auto");
} else {
$("#wish_list").css("height", "35px");
$(this).removeClass("up");
$("#wish_list").css("overflow", "hidden");
}
});
// Remove function
$("#wish_list_item").on("click", ".w-premove", function() {
$office_id = $(this).attr("wpid");
$("#list_id_" + $office_id).remove();
wish_list = [];
$("tr.wishlist-item").each(function(index, el) {
wish_list.push(el.outerHTML);
});
//localStorage.setItem(wishlistkey, JSON.stringify(wish_list));
show_message("Office removed");
count_items_in_wishlist_update();
});
});
//Animation
function show_message($msg) {
$("#msg").html($msg);
$top = Math.max(0, (($(window).height() - $("#msg").outerHeight()) / 2) + $(window).scrollTop()) + "px";
$left = Math.max(0, (($(window).width() - $("#msg").outerWidth()) / 2) + $(window).scrollLeft()) + "px";
$("#msg").css("left", $left);
$("#msg").animate({
opacity: 0.6,
top: $top
}, 400, function() {
$(this).css({
'opacity': 1
});
}).show();
setTimeout(function() {
$("#msg").animate({
opacity: 0.6,
top: "0px"
}, 400, function() {
$(this).hide();
});
}, 2000);
}
//Validation against the amount of product being added
function count_items_in_wishlist_update() {
$("#listitem").html(wish_list.length);
if (wish_list.length > 1) {
$("#p_label").html("Shortlist (");
} else {
$("#p_label").html("Shortlist (");
}
}
button a {
color: #fff !important;
}
button {
font-family: 'open sans', sans-serif;
font-size: 15px;
}
button {
text-align: center;
padding: 1px 7px;
margin: 0 5px 0;
border: 4px solid #000;
background-color: #000;
vertical-align: middle;
cursor: pointer;
white-space: nowrap;
font-size: 35px;
color: #fff;
}
#wish_list {
position: fixed;
bottom: 0px;
right: 0px;
height: 35px;
width: 400px;
background: #fff;
border: 3px solid #22a7c5;
z-index: 3;
}
#wish_list .wish_list_heading {
margin: 0px 0px;
text-align: center;
color: #fff;
height: 27px;
background-color: #22a7c5;
padding: 6px 3px;
font-weight: bold;
cursor: pointer;
font-size: 18px;
}
#wish_list_item {
width: 100%;
text-align: center;
border-spacing: 0px 4px;
border-collapse: separate;
}
#msg {
position: fixed;
display: none;
padding: 10px 25px;
background: #22a7c5;
border: 1px solid #22a7c5;
font-size: 18px;
width: 50%;
text-align: center;
font-weight: 700;
color: #fff;
z-index: 4;
}
.wishlist-item {
padding: 10px 5px;
background: #f1f1f1;
color: #323470;
}
.w-premove {
font-size: 20px;
cursor: pointer;
width: 7%;
padding-bottom: 4px;
}
.w-pname {
font-size: 16px;
width: 93%;
text-align: left;
padding-left: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id='msg'></div>
<div id='wish_list' class='col-s'>
<p class="wish_list_heading">
<i class="fa fa-heart-o"></i>
<span id='p_label'>Shortlist (</span>
<span id='listitem'>0</span>
<span id='p_label'>)</span>
</p>
<table id='wish_list_item' border='0'></table>
</div>
<div class="btn-group">
<button class='wishlist' title="Add To Shortlist" office_name='Hampton Street' office_id='hamptonstreet'>
<span><i class="fa fa-heart">❤️</i></span>
</button>
</div>
Here is an updated version
You need to push the ID and test that.
Also I simplified the count_items_in_wishlist_update
NOTE: Remember to uncomment the localStorage statements when uploading to your server
I update the localstorage only in one place now
Test the code here: https://plungjan.name/SO/lslike/
const offices = {
"hamptonstreet": {
office_name: 'Hampton Street',
office_url: 'https://hamptonstreet.co.uk'
},
"kensingtonstreet": {
office_name: 'Kensington Street',
office_url: 'https://kensingtonstreet.co.uk'
}
};
document.querySelector('.btn-group').innerHTML = Object.entries(offices).map(([key, office]) => `<button class='wishlist' title="Add To Shortlist" office_name='${office.office_name}' office_id='${key}'>
<span><i class="fa fa-heart"></i></span>${office.office_name}</button>`).join('')
const wishlistkey = "wishlist";
// try and fetch an existing wishlist from the localStorage.
let wish_list = null; // localStorage.getItem(wishlistkey) // uncomment and remove null
wish_list = wish_list ? JSON.parse(wish_list) : []
count_items_in_wishlist_update();
$(document).ready(function() {
count_items_in_wishlist_update(); // show what is already in wishlist
$(".wishlist").on("click", function() {
const office_id = $(this).attr("office_id"),
office = offices[office_id];
//check if the element is in the array
const found = $.inArray(office_id, wish_list) > -1
if (found) {
$(`#list_id_${office_id}`).remove()
show_message(office.office_name + " Office Removed");
} else {
wish_list.push(office_id);
show_message(office.office_name + " Office Added");
}
count_items_in_wishlist_update();
});
//adding toggle functionality to the wishlist pannel
$(".wish_list_heading").on("click", function() {
if (!$(this).hasClass("up")) {
$("#wish_list").css("height", "390px");
$(this).addClass("up");
$("#wish_list").css("overflow", "auto");
} else {
$("#wish_list").css("height", "35px");
$(this).removeClass("up");
$("#wish_list").css("overflow", "hidden");
}
});
// Remove function
$("#wish_list_item").on("click", ".w-premove", function() {
office_id = $(this).attr("wpid");
$("#list_id_" + office_id).remove();
wish_list = wish_list.filter(id => id != office_id);
show_message("Office removed");
count_items_in_wishlist_update();
});
});
//Animation
function show_message($msg) {
$("#msg").html($msg);
$top = Math.max(0, (($(window).height() - $("#msg").outerHeight()) / 2) + $(window).scrollTop()) + "px";
$left = Math.max(0, (($(window).width() - $("#msg").outerWidth()) / 2) + $(window).scrollLeft()) + "px";
$("#msg").css("left", $left);
$("#msg").animate({
opacity: 0.6,
top: $top
}, 400, function() {
$(this).css({
'opacity': 1
});
}).show();
setTimeout(function() {
$("#msg").animate({
opacity: 0.6,
top: "0px"
}, 400, function() {
$(this).hide();
});
}, 2000);
}
//Validation against the amount of product being added
function count_items_in_wishlist_update() {
$("#p_label").html(wish_list.length > 0 ? "Shortlist (" + wish_list.length + ")" : "Shortlist");
$('#wish_list_item').empty();
$(".wishlist").each(function(index, el) {
const office_id = $(el).attr("office_id");
const found = wish_list.includes(office_id);
// console.log(office_id,wish_list,found)
const $heart = $(el).find(".fa");
$heart.toggleClass("fa-heart", found);
$heart.toggleClass("fa-heart-o", !found);
if (found) {
const office_id = $(el).attr("office_id"),
office = offices[office_id],
office_str = `<tr class="wishlist-item" id="list_id_${office_id}"><td class="w-pname">${office.office_name}</td><td class="w-premove" wpid="${office_id}">x</td></tr>`;
$('#wish_list_item').append(office_str);
}
})
// localStorage.setItem(wishlistkey, JSON.stringify(wish_list)); // uncomment
}
button a {
color: #fff !important;
}
button {
font-family: 'open sans', sans-serif;
font-size: 15px;
}
button {
text-align: center;
padding: 1px 7px;
margin: 0 5px 0;
border: 4px solid #000;
background-color: #000;
vertical-align: middle;
cursor: pointer;
white-space: nowrap;
font-size: 35px;
color: #fff;
}
#wish_list {
position: fixed;
bottom: 0px;
right: 0px;
height: 35px;
width: 400px;
background: #fff;
border: 3px solid #22a7c5;
z-index: 3;
}
#wish_list .wish_list_heading {
margin: 0px 0px;
text-align: center;
color: #fff;
height: 27px;
background-color: #22a7c5;
padding: 6px 3px;
font-weight: bold;
cursor: pointer;
font-size: 18px;
}
#wish_list_item {
width: 100%;
text-align: center;
border-spacing: 0px 4px;
border-collapse: separate;
}
#msg {
position: fixed;
display: none;
padding: 10px 25px;
background: #22a7c5;
border: 1px solid #22a7c5;
font-size: 18px;
width: 50%;
text-align: center;
font-weight: 700;
color: #fff;
z-index: 4;
}
.wishlist-item {
padding: 10px 5px;
background: #f1f1f1;
color: #323470;
}
.w-premove {
font-size: 20px;
cursor: pointer;
width: 7%;
padding-bottom: 4px;
}
.w-pname {
font-size: 16px;
width: 93%;
text-align: left;
padding-left: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
<div id='msg'></div>
<div id='wish_list' class='col-s'>
<p class="wish_list_heading">
<i class="fa fa-heart-o"></i>
<span id='p_label'>Shortlist</span>
</p>
<table id='wish_list_item' border='0'></table>
</div>
<div class="btn-group">
</div>
No matter how I wrote the code, I received the same message and I've been trying to fix this since last night, but I don't understand why this is bothering me so much.
I get the message that all these lines are wrong
I work according to my course and this code is from a course that works on video and it doesn't work for me, I've tried a lot of changes but it doesn't work.
document.getElementById('dice-1').style.display = 'block';
document.getElementById('dice-2').style.display = 'block';
var scores, roundScore, activePlayer, gamePlaying;
var lastDice;
function init() {
scores = [0, 0];
activePlayer = 0;
roundScore = 0;
gamePlaying = true;
document.getElementById('dice-1').style.display = 'none';
document.getElementById('dice-2').style.display = 'none';
document.getElementById('score-0').textContent = '0';
document.getElementById('score-1').textContent = '0';
document.getElementById('current-0').textContent = '0';
document.getElementById('current-1').textContent = '0';
document.getElementById('name-0').textContent = 'Player 1';
document.getElementById('name-1').textContent = 'Player 2';
document.querySelector('.player-0-panel').classList.remove('winner');
document.querySelector('.player-1-panel').classList.remove('winner');
document.querySelector('.player-0-panel').classList.remove('active');
document.querySelector('.player-1-panel').classList.remove('active');
document.querySelector('.player-0-panel').classList.add('active');
}
document.querySelector('.btn-roll').addEventListener('click', function() {
if(gamePlaying) {
var dice1 = Math.floor(Math.random() * 6) + 1;
var dice2 = Math.floor(Math.random() * 6) + 1;
document.getElementById('dice-1').style.display = 'block';
document.getElementById('dice-2').style.display = 'block';
document.getElementById('dice-1').src = 'dice-' + dice1 + '.png';
document.getElementById('dice-2').src = 'dice-' + dice2 + '.png';
if (dice1 !== 1 && dice2 !== 1) {
roundScore += dice1 + dice2;
document.querySelector('#current-' + activePlayer).textContent = roundScore;
} else {
nextPlayer();
}
}
});
document.querySelector('.btn-hold').addEventListener('click', function() {
if (gamePlaying) {
scores[activePlayer] += roundScore;
document.querySelector('#score-' + activePlayer).textContent = scores[activePlayer];
var input = document.querySelector('.final-score').value;
var winningScore;
if(input) {
winningScore = input;
} else {
winningScore = 100;
}
// Check if player won the game
if (scores[activePlayer] >= winningScore) {
document.querySelector('#name-' + activePlayer).textContent = 'Winner!';
document.getElementById('dice-1').style.display = 'none';
document.getElementById('dice-2').style.display = 'none';
document.querySelector('.player-' + activePlayer + '-panel').classList.add('winner');
document.querySelector('.player-' + activePlayer + '-panel').classList.remove('active');
gamePlaying = false;
} else {
nextPlayer();
}
}
});
function nextPlayer() {
activePlayer === 0 ? activePlayer = 1 : activePlayer = 0;
roundScore = 0;
document.getElementById('current-0').textContent = '0';
document.getElementById('current-1').textContent = '0';
document.querySelector('.player-0-panel').classList.toggle('active');
document.querySelector('.player-1-panel').classList.toggle('active');
document.getElementById('dice-1').style.display = 'none';
document.getElementById('dice-2').style.display = 'none';
}
document.querySelector('.btn-new').addEventListener('click', init);
init();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css?family=Lato:100,300,600" rel="stylesheet" type="text/css">
<link href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css">
<link type="text/css" rel="stylesheet" href="style.css">
<title>Pig Game</title>
</head>
<body>
<div class="wrapper clearfix">
<div class="player-0-panel active">
<div class="player-name" id="name-0">Player 1</div>
<div class="player-score" id="score-0">43</div>
<div class="player-current-box">
<div class="player-current-label">Current</div>
<div class="player-current-score" id="current-0">11</div>
</div>
</div>
<div class="player-1-panel">
<div class="player-name" id="name-1">Player 2</div>
<div class="player-score" id="score-1">72</div>
<div class="player-current-box">
<div class="player-current-label">Current</div>
<div class="player-current-score" id="current-1">0</div>
</div>
</div>
<button class="btn-new"><i class="ion-ios-plus-outline"></i>New game</button>
<button class="btn-roll"><i class="ion-ios-loop"></i>Roll dice</button>
<button class="btn-hold"><i class="ion-ios-download-outline"></i>Hold</button>
<input type="text" placeholder="Final Score" class="final-score">
<img src="dice-5.png" alt="Dice" class="dice">
</div>
<script type='text/javascript' src="challenges.js"></script>
</body>
</html>
/**********************************************
*** GENERAL
**********************************************/
.final-score {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: 520px;
color: #555;
font-size: 18px;
font-family: 'Lato';
text-align: center;
padding: 10px;
width: 160px;
text-transform: uppercase;
}
.final-score:focus { outline: none; }
dice-1 { top: 120px; }
#dice-2 { top: 250px; }
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.clearfix::after {
content: "";
display: table;
clear: both;
}
body {
background-image: linear-gradient(rgba(62, 20, 20, 0.4), rgba(62, 20, 20, 0.4)), url(back.jpg);
background-size: cover;
background-position: center;
font-family: Lato;
font-weight: 300;
position: relative;
height: 100vh;
color: #555;
}
.wrapper {
width: 1000px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
box-shadow: 0px 10px 50px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
.player-0-panel,
.player-1-panel {
width: 50%;
float: left;
height: 600px;
padding: 100px;
}
/**********************************************
*** PLAYERS
**********************************************/
.player-name {
font-size: 40px;
text-align: center;
text-transform: uppercase;
letter-spacing: 2px;
font-weight: 100;
margin-top: 20px;
margin-bottom: 10px;
position: relative;
}
.player-score {
text-align: center;
font-size: 80px;
font-weight: 100;
color: #EB4D4D;
margin-bottom: 130px;
}
.active { background-color: #f7f7f7; }
.active .player-name { font-weight: 300; }
.active .player-name::after {
content: "\2022";
font-size: 47px;
position: absolute;
color: #EB4D4D;
top: -7px;
right: 10px;
}
.player-current-box {
background-color: #EB4D4D;
color: #fff;
width: 40%;
margin: 0 auto;
padding: 12px;
text-align: center;
}
.player-current-label {
text-transform: uppercase;
margin-bottom: 10px;
font-size: 12px;
color: #222;
}
.player-current-score {
font-size: 30px;
}
button {
position: absolute;
width: 200px;
left: 50%;
transform: translateX(-50%);
color: #555;
background: none;
border: none;
font-family: Lato;
font-size: 20px;
text-transform: uppercase;
cursor: pointer;
font-weight: 300;
transition: background-color 0.3s, color 0.3s;
}
button:hover { font-weight: 600; }
button:hover i { margin-right: 20px; }
button:focus {
outline: none;
}
i {
color: #EB4D4D;
display: inline-block;
margin-right: 15px;
font-size: 32px;
line-height: 1;
vertical-align: text-top;
margin-top: -4px;
transition: margin 0.3s;
}
.btn-new { top: 45px;}
.btn-roll { top: 403px;}
.btn-hold { top: 467px;}
.dice {
position: absolute;
left: 50%;
top: 178px;
transform: translateX(-50%);
height: 100px;
box-shadow: 0px 10px 60px rgba(0, 0, 0, 0.10);
}
.winner { background-color: #f7f7f7; }
.winner .player-name { font-weight: 300; color: #EB4D4D; }
Please help me.
İn javascript , remove init(); or define init function.
try this:
function init() {
scores = [0, 0];
activePlayer = 0;
roundScore = 0;
gamePlaying = true;
document.getElementById('dice-1').style.display = 'none';
document.getElementById('dice-2').style.display = 'none';
document.getElementById('score-0').textContent = '0';
document.getElementById('score-1').textContent = '0';
document.getElementById('current-0').textContent = '0';
document.getElementById('current-1').textContent = '0';
document.getElementById('name-0').textContent = 'Player 1';
document.getElementById('name-1').textContent = 'Player 2';
document.querySelector('.player-0-panel').classList.remove('winner');
document.querySelector('.player-1-panel').classList.remove('winner');
document.querySelector('.player-0-panel').classList.remove('active');
document.querySelector('.player-1-panel').classList.remove('active');
document.querySelector('.player-0-panel').classList.add('active');
}
document.querySelector('.btn-roll').addEventListener('click', function() {
if(gamePlaying) {
var dice1 = Math.floor(Math.random() * 6) + 1;
var dice2 = Math.floor(Math.random() * 6) + 1;
document.getElementById('dice-1').style.display = 'block';
document.getElementById('dice-2').style.display = 'block';
document.getElementById('dice-1').src = 'dice-' + dice1 + '.png';
document.getElementById('dice-2').src = 'dice-' + dice2 + '.png';
if (dice1 !== 1 && dice2 !== 1) {
roundScore += dice1 + dice2;
document.querySelector('#current-' + activePlayer).textContent = roundScore;
} else {
nextPlayer();
}
}
});
document.querySelector('.btn-hold').addEventListener('click', function() {
if (gamePlaying) {
scores[activePlayer] += roundScore;
document.querySelector('#score-' + activePlayer).textContent = scores[activePlayer];
var input = document.querySelector('.final-score').value;
var winningScore;
if(input) {
winningScore = input;
} else {
winningScore = 100;
}
// Check if player won the game
if (scores[activePlayer] >= winningScore) {
document.querySelector('#name-' + activePlayer).textContent = 'Winner!';
document.getElementById('dice-1').style.display = 'none';
document.getElementById('dice-2').style.display = 'none';
document.querySelector('.player-' + activePlayer + '-panel').classList.add('winner');
document.querySelector('.player-' + activePlayer + '-panel').classList.remove('active');
gamePlaying = false;
} else {
nextPlayer();
}
}
});
function nextPlayer() {
activePlayer === 0 ? activePlayer = 1 : activePlayer = 0;
roundScore = 0;
document.getElementById('current-0').textContent = '0';
document.getElementById('current-1').textContent = '0';
document.querySelector('.player-0-panel').classList.toggle('active');
document.querySelector('.player-1-panel').classList.toggle('active');
document.getElementById('dice-1').style.display = 'none';
document.getElementById('dice-2').style.display = 'none';
}
document.querySelector('.btn-new').addEventListener('click', init);
function init(){
}
I don't see any HTML elements with id="dice-1" nor id="dice-2", which would explain the error you're receiving.
I don't see element with id="dice-1" or id="dice-2" in your template (HTML), so the error just means, that element with that id doesn't exist, so it's value is null.
I have created a todo-list following watchandcode.com course and I tried to add more functionality to it. So I want to make the COMPLETED button which toggles the non-completed list items ad completed,DISAPPEAR after I click on it and after it turns the list item into completed. I hope I expressed my self properly of what I'd like to achieve. Here's the code down below, thanks in advance!
I copied the whole code down below, but if you only need to see the parts that I might be referring (I am not sure, as I am really new into JS), here they are:
var view = {
displayTodos: function(){
var todosUl = document.querySelector("ul");
todosUl.innerHTML = '';
for (var i = 0; i < todoList.todos.length; i++){
var todoLi = document.createElement("li");
var todo = todoList.todos[i];
var todoTextForCompleted = "";
if (todo.completed === true){
todoTextForCompleted = todo.todoText;
todoLi.classList.toggle("checked");
} else {
todoTextForCompleted = todo.todoText;
todoLi.classList.toggle("notCompleted");
}
todoLi.id = i;
todoLi.textContent = todoTextForCompleted;
todoLi.appendChild(this.createDeleteButton());
todoLi.appendChild(this.createToggleButton());
todosUl.appendChild(todoLi);
}
},
createToggleButton: function(){
var toggleButton = document.createElement("button");
toggleButton.textContent = "Completed";
toggleButton.id= "toggleBtn";
return toggleButton;
},
Here's the whole code if its necessary:
//The object that holds the todo list:
var todoList = {
todos: [],
//Method for ADDING a TODO items
addTodo: function(todoText){
this.todos.push({
todoText: todoText,
completed: false,
});
},
//Method for changing the made TODO items
changeTodo: function(position, newValue){
this.todos[position].todoText = newValue;
},
//Method for deleting TODO items:
deleteTodo: function(position){
this.todos.splice(position, 1);
},
//Method for toggling a todo item as COMPLETED:
toggleCompleted: function(position){
var todo = this.todos[position];
todo.completed = !todo.completed;
},
//Method for toggling ALL todo items:
toggleAll: function(){
var totalTodos = this.todos.length;
//Counting the ones that are completed
var completedTodos = 0;
//Getting the number of the completed todos:
for(var i = 0; i < totalTodos; i++){
if(this.todos[i].completed === true) {
//For every todo item that is completed, increase completedTodos by 1;
completedTodos++;
}
}
//Case 1: If everything is true, the turn it into FALSE;
if(completedTodos === totalTodos){
for(var i = 0; i < totalTodos; i++){
this.todos[i].completed = false
}
//Case 2: Otherwise, make everything TRUE;
} else {
for (var i = 0; i < totalTodos; i++){
this.todos[i].completed = true;
};
};
}
}
//Object for the BUTTON HANDLERS:
var handlers = {
addTodo: function(){
var addTodoText = document.getElementById("addTodoTextInput");
todoList.addTodo(addTodoTextInput.value);
//Clear the input
addTodoText.value = "";
view.displayTodos();
},
//Change todo
changeTodo: function(){
var changeTodoText = document.getElementById("changeTodoText");
var changeTodoPosition = document.getElementById("changeTodoPosition");
//Get the CHANGE TODO method in the object TODOLIST and set these parameters
todoList.changeTodo(changeTodoPosition.valueAsNumber, changeTodoText.value);
//Clear the inputs
changeTodoText.value = "";
changeTodoPosition.value = "";
//Call upon VIEW object and trigger displayTodos() method/function;
view.displayTodos();
},
deleteTodo: function(position){
todoList.deleteTodo(position);
view.displayTodos();
},
deleteAll:function(position){
todoList.todos.splice(position);
view.displayTodos();
},
toggleCompleted: function(){
var toggleCompletedInput = document.getElementById("toggleBtn");
todoList.toggleCompleted(toggleCompletedInput.valueAsNumber);
toggleCompletedInput.value = "";
view.displayTodos();
},
toggleAll: function(){
todoList.toggleAll();
view.displayTodos();
}
};
//Object that is used only to DISPLAY the items:
var view = {
displayTodos: function(){
var todosUl = document.querySelector("ul");
todosUl.innerHTML = '';
for (var i = 0; i < todoList.todos.length; i++){
var todoLi = document.createElement("li");
var todo = todoList.todos[i];
var todoTextForCompleted = "";
if (todo.completed === true){
todoTextForCompleted = todo.todoText;
todoLi.classList.toggle("checked");
} else {
todoTextForCompleted = todo.todoText;
todoLi.classList.toggle("notCompleted");
}
todoLi.id = i;
todoLi.textContent = todoTextForCompleted;
todoLi.appendChild(this.createDeleteButton());
todoLi.appendChild(this.createToggleButton());
todosUl.appendChild(todoLi);
}
},
createDeleteButton: function(){
var deleteButton = document.createElement("button");
deleteButton.textContent = "DELETE";
deleteButton.className = "deleteBtn";
return deleteButton;
},
createToggleButton: function(){
var toggleButton = document.createElement("button");
toggleButton.textContent = "Completed";
toggleButton.id= "toggleBtn";
return toggleButton;
},
//
setUpEventListeners: function(){
var todosUl = document.querySelector("ul");
todosUl.addEventListener("click", function(event){
var elementClicked = event.target;
if(elementClicked.className === "deleteBtn"){
handlers.deleteTodo(parseInt(elementClicked.parentNode.id));
}
if (elementClicked.id === "toggleBtn"){
todoList.toggleCompleted(parseInt(elementClicked.parentNode.id));
var toggleCompletedInput = document.getElementById("toggleBtn");
toggleCompletedInput.value = "";
view.displayTodos();
view.removeTodoButton();
}
});
}
}
view.setUpEventListeners();
The code was a bit messy... the main problem I saw is that you were using multiple elements with same id (toggleBtn). I changed it to use a className. Here you have a working version following the design pattern (view-model-handlers).
The changes are:
toggleCompleted: function(position) {
var toggleCompletedInput = document.querySelector("#" + position + " toggleBtn");
var todoLi = document.createElement("li");
var todo = todoList.todos[i];
todoLi.id = i;
todoLi.textContent = todo.todoText;
if (todo.completed === true) {
todoLi.classList.toggle("checked");
} else {
todoLi.classList.toggle("notCompleted");
todoLi.appendChild(this.createToggleButton());
}
todoLi.appendChild(this.createDeleteButton());
todosUl.appendChild(todoLi);
toggleButton.className = "toggleBtn";
if (elementClicked.className === "toggleBtn") {
todoList.toggleCompleted(parseInt(elementClicked.parentNode.id));
view.displayTodos();
}
And in CSS:
.toggleBtn {
background-color: #eee;
color: #e84118;
cursor: pointer;
position: absolute;
right: 70px;
bottom: 3px;
animation: delete 0.5s ease-out 1 forwards;
}
.toggleBtn:hover {
background-color: #4cd137;
color: #eee;
}
.toggleBtn.removed {
display: none;
}
//The object that holds the todo list:
var todoList = {
todos: [],
//Method for ADDING a TODO items
addTodo: function(todoText) {
this.todos.push({
todoText: todoText,
completed: false,
});
},
//Method for changing the made TODO items
changeTodo: function(position, newValue) {
this.todos[position].todoText = newValue;
},
//Method for deleting TODO items:
deleteTodo: function(position) {
this.todos.splice(position, 1);
},
//Method for toggling a todo item as COMPLETED:
toggleCompleted: function(position) {
var todo = this.todos[position];
todo.completed = !todo.completed;
},
//Method for toggling ALL todo items:
toggleAll: function() {
var totalTodos = this.todos.length;
//Counting the ones that are completed
var completedTodos = 0;
//Getting the number of the completed todos:
for (var i = 0; i < totalTodos; i++) {
if (this.todos[i].completed === true) {
//For every todo item that is completed, increase completedTodos by 1;
completedTodos++;
}
}
//Case 1: If everything is true, the turn it into FALSE;
if (completedTodos === totalTodos) {
for (var i = 0; i < totalTodos; i++) {
this.todos[i].completed = false
}
//Case 2: Otherwise, make everything TRUE;
} else {
for (var i = 0; i < totalTodos; i++) {
this.todos[i].completed = true;
};
};
}
}
//Object for the BUTTON HANDLERS:
var handlers = {
addTodo: function() {
var addTodoText = document.getElementById("addTodoTextInput");
todoList.addTodo(addTodoTextInput.value);
//Clear the input
addTodoText.value = "";
view.displayTodos();
},
//Change todo
changeTodo: function() {
var changeTodoText = document.getElementById("changeTodoText");
var changeTodoPosition = document.getElementById("changeTodoPosition");
//Get the CHANGE TODO method in the object TODOLIST and set these parameters
todoList.changeTodo(changeTodoPosition.valueAsNumber, changeTodoText.value);
//Clear the inputs
changeTodoText.value = "";
changeTodoPosition.value = "";
//Call upon VIEW object and trigger displayTodos() method/function;
view.displayTodos();
},
deleteTodo: function(position) {
todoList.deleteTodo(position);
view.displayTodos();
},
deleteAll: function(position) {
todoList.todos.splice(position);
view.displayTodos();
},
toggleCompleted: function(position) {
var toggleCompletedInput = document.querySelector("#" + position + " toggleBtn");
todoList.toggleCompleted(toggleCompletedInput.valueAsNumber);
toggleCompletedInput.value = "";
view.displayTodos();
},
toggleAll: function() {
todoList.toggleAll();
view.displayTodos();
}
};
//Object that is used only to DISPLAY the items:
var view = {
displayTodos: function() {
var todosUl = document.querySelector("ul");
todosUl.innerHTML = '';
for (var i = 0; i < todoList.todos.length; i++) {
var todoLi = document.createElement("li");
var todo = todoList.todos[i];
todoLi.id = i;
todoLi.textContent = todo.todoText;
if (todo.completed === true) {
todoLi.classList.toggle("checked");
} else {
todoLi.classList.toggle("notCompleted");
todoLi.appendChild(this.createToggleButton());
}
todoLi.appendChild(this.createDeleteButton());
todosUl.appendChild(todoLi);
}
},
createDeleteButton: function() {
var deleteButton = document.createElement("button");
deleteButton.textContent = "DELETE";
deleteButton.className = "deleteBtn";
return deleteButton;
},
createToggleButton: function() {
var toggleButton = document.createElement("button");
toggleButton.textContent = "Completed";
toggleButton.className = "toggleBtn";
return toggleButton;
},
//
setUpEventListeners: function() {
var todosUl = document.querySelector("ul");
todosUl.addEventListener("click", function(event) {
var elementClicked = event.target;
if (elementClicked.className === "deleteBtn") {
handlers.deleteTodo(parseInt(elementClicked.parentNode.id));
}
if (elementClicked.className === "toggleBtn") {
todoList.toggleCompleted(parseInt(elementClicked.parentNode.id));
view.displayTodos();
}
});
}
}
view.setUpEventListeners();
* {
margin: 0;
padding: 0;
}
body {
background-color: #dcdde1;
font-family: 'Poppins', sans-serif;
}
li {
color: #eee;
margin: 5px 0;
list-style-type: none;
padding: 10px;
background-color: grey;
display: flex;
justify-content: flex-start;
align-items: center;
user-select: none;
width: 100%;
animation: listitem 1s 1 forwards;
position: relative;
border-radius: 5px;
}
li.deleted {
animation: listItem 1s 1;
}
#keyframes listItem {
from {
opacity: 0
}
to {
opacity: 1
}
;
}
li.checked {
background-color: #4cd137;
color: #eee;
}
li.checked:before {
color: #eee;
margin: 0 10px;
content: "\2714";
animation: checkmark 1s 1 forwards;
}
#keyframes checkmark {
0% {
transform: scale(0);
}
60% {
transform: scale(1.3);
}
100% {
transform: scale(1);
}
}
li.notCompleted {
background-color: #e84118;
color: #eee;
}
li.notCompleted:before {
content: "\2716";
animation: check 1s 1 forwards;
margin: 0 10px;
}
#keyframes check {
0% {
transform: scale(0);
}
60% {
transform: scale(1.3);
}
100% {
transform: scale(1);
}
}
ul li .deleteBtn {
background-color: #eee;
color: #e84118;
cursor: pointer;
position: absolute;
right: 10px;
bottom: 3px;
animation: delete 0.5s ease-out 1 forwards;
}
.deleteBtn:hover {
background-color: #353b48;
color: #eee;
}
.toggleBtn {
background-color: #eee;
color: #e84118;
cursor: pointer;
position: absolute;
right: 70px;
bottom: 3px;
animation: delete 0.5s ease-out 1 forwards;
}
.toggleBtn:hover {
background-color: #4cd137;
color: #eee;
}
.toggleBtn.removed {
display: none;
}
#keyframes delete {
0% {
transform: scale(0);
}
50% {
transform: scale(1.05);
}
80% {
transform: scale(0.95);
}
100% {
transform: scale(1);
}
}
.wrapper {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 10px;
}
h1 {
color: #e84118;
font-size: 35px;
font-weight: 700;
text-transform: uppercase;
}
h1 span {
color: #0097e6;
font-size: 65px;
font-weight: 800;
opacity: 0;
letter-spacing: -50px;
animation: headerSpan 2s 0.5s 1 forwards;
}
#keyframes headerSpan {
0% {
opacity: 0;
letter-spacing: -50px;
transform: scale(0);
}
50% {
letter-spacing: 10px;
}
100% {
opacity: 1;
transform: scale(1);
letter-spacing: 0px;
}
}
input {
padding: 5px;
border: none;
background-color: #f5f6fa;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
border-radius: 3px;
color: #e84118;
font-family: 'Poppins';
width: 100%;
margin: 2px 0;
}
input:focus {
outline: none !important;
}
input::placeholder {
color: #e84118;
font-family: "Poppins";
font-style: italic;
font-size: 12px;
padding-left: 5px;
}
button {
margin: 5px 0;
padding: 3px;
border: none;
border-radius: 3px;
font-family: 'Poppins';
text-transform: uppercase;
color: #eee;
background-color: #00a8ff;
font-weight: bold;
font-size: 14px;
transition: 0.2s ease-out;
}
button:focus {
outline: none!important;
}
button:hover {
background-color: #e84118;
color: #eee;
}
.container_todo {
border-radius: 5px;
padding: 35px;
display: flex;
flex-direction: column;
background-color: #f4f4f4;
box-shadow: 0 2px 25px rgba(0, 0, 0, 0.25);
animation: container 1s 1 forwards;
}
.toggleAllButtons {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.all.active {
background-color: red;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Todo List</title>
<link rel="stylesheet" type="text/css" href="styles.css">
<link href="https://fonts.googleapis.com/css?family=Gaegu|Indie+Flower|Pacifico|Raleway" rel="stylesheet">
</head>
<body>
<div class="wrapper">
<!-- Title -->
<h1>The <span>Todo</span> List !</h1>
<!-- TODO LIST container -->
<div class="container_todo">
<!-- BUTTONS -->
<!-- Add Todo item -->
<div class="add">
<button onclick="handlers.addTodo()">Add todo</button>
<input type="text" id="addTodoTextInput" placeholder="Add a Todo item">
</div>
<!-- Change Todo Item-->
<div class="change">
<button onclick="handlers.changeTodo()">Change Todo</button>
<input type="text" id="changeTodoText" placeholder="Change a Todo item">
<input type="number" id="changeTodoPosition" placeholder="Choose which Item to change">
</div>
<!-- Toggle completed Todo item -->
<div class="toggleAllButtons">
<!-- Toggle All items button -->
<div class="all">
<button onclick="handlers.toggleAll()">Toggle All</button>
<button onclick="handlers.deleteAll()">Delete All</button>
</div>
</div>
<ul></ul>
</div>
</div>
<!-- /////////////////////////////////////// -->
<!-- JS link -->
<script src="script.js"></script>
</body>
</html>
In google maps Api for javaScript V3 , it's possible show dialog same as location dialog
Example:
When click some marker:
marker.addListener('click', function () {
// console.log(marcador['geo']);
mostrarInfoMarcas(marcador['user'], marcador['geo']);
});
Here is my solution,
I create the marker by OverlayView Object
So You can treat it as DIV and input the HTML content
function CustomMarker(latlng, map, args) {
this.latlng = latlng;
this.args = args;
this.setMap(map);
}
CustomMarker.prototype = new google.maps.OverlayView();
CustomMarker.prototype.draw = function() {
var self = this;
var div = this.div;
if (!div) {
div = this.div = document.createElement('div');
var style = '';
var content = '';
if (typeof(self.args.marker_style) !== 'undefined') {
style = self.args.marker_style;
div.className = 'iot-node-'+style;
}
if (typeof(self.args.marker_content) !== 'undefined') {
content = self.args.marker_content;
var a = document.createElement('a');
var cur = this.getPosition();
a.style.backgroundColor='blue';
a.style.color = 'white';
a.style.fontSize = "3em";
a.style.textAlign= "center";
a.innerHTML = (content=="-1"?"-":content);
var sub_div = document.createElement('div');
sub_div.style.paddingLeft = '2px';
var sub_info = self.args.marker_info;
sinfo = '<BR>'+sub_info.name+'<BR>'+
'<font size=2>'+sub_info.office+'</font>';
sub_div.innerHTML = sinfo;
sub_div.className = 'showme';
sub_div.style.lineHeight= "16px";
sub_div.style.backgroundColor = 'blue';
sub_div.style.color = 'white';
a.appendChild(sub_div);
div.appendChild(a);
if(style=='info') {
var span = document.createElement('span');
span.innerHTML = 'x';
google.maps.event.addDomListener(span, "click", function(event) {
});
//a.appendChild(span);
}
}
if (typeof(self.args.marker_style) !== 'undefined' && style!='') {
var cur = this.getPosition();
var me = this;
var panes = this.getPanes();
panes.overlayImage.appendChild(div);
var point = this.getProjection().fromLatLngToDivPixel(this.getPosition());
if (point) {
if(style!='info') {
div.style.left = (point.x-20 ) + 'px';
div.style.top = (point.y-10) + 'px';
var my_info= null;
var sub = {latLng: cur, style: 'info', content: root_content.replace('[type]', style.toUpperCase()).replace('[address]', addr)};
my_info= create_node(map, sub);
nd.push(my_info);
} else {
div.style.left = (point.x - 35 ) + 'px';
div.style.top = (point.y - 60 ) + 'px';
}
}
}
}
};
CustomMarker.prototype.remove = function() {
if (this.div) {
this.div.parentNode.removeChild(this.div);
this.div = null;
}
this.setMap(null);
};
CustomMarker.prototype.getPosition = function() {
return this.latlng;
};
function create_node(map, node) {
return new CustomMarker(
node.latLng,
map,
{
marker_style: node.style,
marker_content: node.content,
marker_info: node.info
}
);
}
var map;
function initialize() {
var centerLat = 21.04731; //default lat
var centerLng = 105.792137; //default long
var cen = new google.maps.LatLng(centerLat, centerLng) ;
map = new google.maps.Map(document.getElementById('map'), {
zoom: 17,
center: cen,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
Draw();
}
var nd = [];
function Draw() {
var lastest_json='{"markers":[{"markerId":"1","name":"Marker1","info":"INFO1","lat":"21.04731","lon":"105.792137"}]}'
var json_dev = JSON.parse(lastest_json);
var markers = json_dev.markers;
for(var i=0; i<markers.length; i++)
{
var dev = markers[i];
var cen = new google.maps.LatLng(parseFloat(dev.lat), parseFloat(dev.lon));
var sub = {latLng: cen, style: 'info', info: dev, content: dev.markerId};
nd.push(create_node(map, sub));
}
}
google.maps.event.addDomListener(window, 'load',initialize);
.iot-node-ae, .iot-node-asn, .iot-node-adn, .iot-node-mn, .iot-node-in, .iot-node-info {
position: absolute;
list-style-type: none;
left: 20px;
top: 20px;
}
.iot-node-ae>a, .iot-node-asn>a, .iot-node-adn>a, .iot-node-mn>a, .iot-node-in>a, .iot-node-info>a {
position: relative;
z-index: 2;
display: block;
width: 20px;
height: 20px;
border-style: solid;
border-width: 2px;
border-color: #0079ff;
border-radius: 50%;
background: white;
text-align:left;
font-weight: bold;
padding-top:0px;
padding-left:0px;
padding-right:0px;
-webkit-transition: background-color 0.2s;
-moz-transition: background-color 0.2s;
-o-transition: background-color 0.2s;
transition: background-color 0.2s;
}
.iot-node-info>a {
width: 60px;
height: 35px;
position: relative;
border-radius: 0;
}
.iot-node-info>a>span {
position: absolute; top: 2px; right:2px;
cursor: pointer;
display:inline-block;
padding:2px 5px;
background:#ccc;
}
.iot-node-info>a>span::hover {
position: absolute; top: 2px; right:2px;
cursor: pointer;
display:inline-block;
padding:2px 5px;
background:#ccc;
}
.iot-node-info>a::before {
content:"";
position: absolute;
top: 100%;
left: 0;
width: 0;
height: 0;
border-left: 13px solid transparent;
border-right: 26px solid #0079ff;
border-bottom: 13px solid transparent;
}
.iot-node-info>a>div {
display:none;
}
.iot-node-info>a:hover > .showme{
width: 150px;
height: 70px;
background-color:white;
position:absolute;
top:-70px;
left:-45px;
border-width:2px;
border-color:#0079ff;
display:block;
}
.iot-node-ae::after, .iot-node-asn::after , .iot-node-adn::after , .iot-node-mn::after , .iot-node-in::after , .iot-node-info::after{
content: '';
position: absolute;
border-radius: 50%;
width: 100%;
height: 100%;
top: 0;
left: 0;
//animation: cd-pulse 2s infinite;
}
#keyframes cd-pulse
{
0% {box-shadow:0 0 0 0 #0079ff}
100%{box-shadow:0 0 0 20px rgba(255,150,44,0)}
}
/* AE */
.iot-node-ae{
}
.iot-node-ae>a{
background: #ff3300;
font-size:14px;
}
.iot-node-ae::after{
//animation: cd-pulse 2s infinite;
}
/* asn */
.iot-node-asn{
}
.iot-node-asn>a{
background: #cc9900;
}
.iot-node-asn::after{
//animation: cd-pulse 2s infinite;
}
/* adn */
.iot-node-adn{
}
.iot-node-adn>a{
background: #330099;
}
.iot-node-adn::after{
//animation: cd-pulse 2s infinite;
}
/* mn */
.iot-node-mn{
}
.iot-node-mn>a{
background: #669900;
}
.iot-node-mn::after{
//animation: cd-pulse 2s infinite;
}
/* IN */
.iot-node-in{
}
.iot-node-in>a{
background: #ff9900;
font-size:14px;
}
.iot-node-in::after{
//animation: cd-pulse 2s infinite;
}
<script src="http://maps.google.com/maps/api/js" type="text/javascript"></script>
<div id="map" style="width: 100%; height: 238px; "></div>