How to create a CSS Grid Scheduler? Angular - javascript

How can I create a calendar with Angular where the first row is the places, the first column is the hours?
And the rest of the cells a space to put events.
For now I have this but I have the following problems,
-How can I add a border and the other thing is how do I place the rows of hours and places go hand in hand with the grid.
-My other problem is that the hours and places are dynamic when I create the grid template I have the values fixed.
HTML:
<div class="main-container">
<div class="stages-container-row">
<div>
<div *ngFor="let place of places">{{ place }}</div>
</div>
</div>
<div class="timing-container-column">
<div class="timings">
<div class="time">Time</div>
<div class="time" *ngFor="let hour of hours">
<span class="hour">
{{ hour }}
</span>
</div>
</div>
</div>
<ng-container *ngFor="let event of events">
<div class="slot" [ngStyle]="event.position">
<p>{{ event.location }}</p>
</div>
</ng-container>
CSS:
.main-container {
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-rows: repeat(49, 1fr);
}
.stages-container-row {
grid-column: 2 / -1;
grid-row: 1 / 2;
}
[class^='stage']:nth-child(n + 2) {
grid-column: n + 2;
}
.details {
display: flex;
flex-direction: column;
}
.timing-container-column {
grid-column: 1 / 2;
grid-row: 1 / -1;
}
.timings {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.time {
display: flex;
justify-content: center;
align-items: center;
}
.slot {
flex-direction: column;
border: 1px solid red;
}
TS:
export class AppComponent implements AfterViewInit, OnInit {
interval = 30;
hourInit = 0;
minuteInit = 0;
hourEnd = 24;
minuteEnd = 0;
hours = [];
places = ['P1', 'P2', 'P3', 'P4', 'P5', 'P6'];
events: any = [];
constructor() {}
ngOnInit(): void {
this.generateHours();
}
generateHours() {
const start = this.hourInit * 60 + this.minuteInit;
const end = this.hourEnd * 60;
const periods = [];
for (let i = start; i < end; i += this.interval) {
periods.push(i);
}
this.hours = periods.map((period) => {
let hour =
Math.floor(period / 60)
.toString()
.padStart(2, '0') +
':' +
(period % 60).toString().padStart(2, '0');
return hour;
});
}
ngAfterViewInit() {
this.createEvent(1, 4, 'P3');
this.createEvent(16, 3, 'P1');
}
createEvent(period: number, duration: number, location: string) {
const event: any = { period, duration, location };
event.position = this.calculateEventPosition(period, duration, location);
this.events.push(event);
}
calculateEventPosition(period: number, duration: number, location: string) {
let position = {};
// Calculate start time of event
let startIndex = period - 1;
let startRow = startIndex + 2;
// Calculate end time of event
let endIndex = startIndex + duration;
let endRow = endIndex + 1;
// Calculate location of event
let locationIndex = this.places.indexOf(location);
let locationColumn = locationIndex + 1;
// Set grid-row position of event
position['grid-row'] = startRow + ' / ' + endRow;
// Set grid-column position of event
position['grid-column'] = locationColumn + ' / ' + (locationColumn + 1);
return position;
}
}
I plan to achieve what is in the image, but I ran into these problems.

Related

I would like some help displaying js stuff on the html side of things

I am attempting to display my snake on this dynamic gameboard i made as well as randomly place food
on said board how would i go about turning my js into visual html.
now that i think about it i havent even made a tick function
I've stored the game board and would like to place food randomly within said gameboard
i would assume it would involve toggling a class tied to some styling in css but i do not know how to execute
const gameData = {
getSize(){
let sizeInput = document.querySelector('#sizeInput').value;
sizeInput = parseInt(sizeInput)
console.log(sizeInput)
this.validateBoard(sizeInput)
return sizeInput
},
validateBoard(size){
if(size === NaN || size < 10 || size > 50){
console.log('not good')
return false;
}else if(size === '' || size >= 10 && size <= 50){
console.log('good good')
this.boardSize = size
return true;
}
},
boardSize: 0,
countDownTimer(timer){
if (timer >= 0) {
message.textContent = 'Rendering ' + timer;
timer--
}
return timer;
},
inputMessage(){
let message = document.querySelector('#message')
let validBoard = gameData.validateBoard(gameData.getSize())
console.log(validBoard)
if(validBoard) {
let timer = 5
let countDown = setInterval(function(){
console.log(countDown)
if(timer === 0){
clearInterval(countDown)
generateGameBoard();
}
timer = gameData.countDownTimer(timer)
}, 1000)
} else if(!validBoard){
return message.textContent = 'Invalid entry. Try Again!'
}
},
gameBoard: [],
//currentSnake: [[x,y]],
snakePos: {
x: 4,
y: 4
},
food:[],
//directionFacing: [];
randomfoodSquare(){
let boardSize = this.boardSize
let squareX = Math.floor(Math.random() * (boardSize + 1))
let squareY = Math.floor(Math.random() * (boardSize + 1))
this.food = [squareX, squareY]
}
}
function generateFood(){
randomFoodSquare();
}
let submitSize = document.querySelector('#submit')
function generateGameBoard(){
let sizeInput = gameData.boardSize
let startMenu = document.querySelector('#startMenu')
let table = document.querySelector('#tb')
startMenu.style.display = 'none'
table.style.display = 'flex'
console.log(sizeInput)
for (let i = 0; i < sizeInput; i++) {
console.log(i)
let row = document.createElement('tr');
for(let j = 0; j < sizeInput; j++){
let square = document.createElement('td')
row.appendChild(square)
let x = j
let y = i
gameData.gameBoard.push([x,y])
}
table.appendChild(row)
}
adjustBoardSize();
console.log(gameData.gameBoard)
}
function adjustBoardSize(){
let sizeInput = gameData.boardSize;
let table = document.querySelector('#tb')
let row = document.querySelectorAll('tr')
let square = document.querySelectorAll('td');
let tableWidth = table.offsetWidth;
let tableHeight = table.offsetHeight;
let rowWidth = tableWidth;
row.forEach( rowEl => {
rowEl.style.height = tableHeight/sizeInput + 'px'
})
square.forEach( tdEl=> {
tdEl.style.width = rowWidth/sizeInput + 'px'
})
}
submitSize.addEventListener('click', function(){
gameData.inputMessage();
let sizeInput = document.querySelector('#sizeInput')
sizeInput.value = '';
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<div id="startMenu" class="hidden">
<h1>Beasteds Snake Game</h1>
<h2 id="message">Choose your board size!</h2>
<input id="sizeInput" type="text" placeholder="ex. 20 will give 20*20 grid">
<input id='submit'type="button">
</div>
<table id="tb">
</table>
<script src="snakeGame.js"></script>
</body>
</html>
```
* {
box-sizing: border-box;
}
body{
background-color: cadetblue;
}
#tb{
display: none;
margin: 0 auto;
flex-wrap: wrap;
text-align: center;
border: black solid 1px;
height: 600px;
width: 600px;
}
#startMenu{
display: block;
justify-content: center;
text-align: center;
margin-top: 200px ;
background-color: cyan;
}
td {
background-color: black;
display: flex;
flex-wrap: wrap;
border: rgb(250, 251, 255) solid 1px;
}
tr {
display: flex;
width: 100%;
}
#snakeHead{
color: darkblue;
}

How to add timer inside the progress bar

I want to add a timer which decrease Automatically (like 10 seconds, 9 seconds... till 0 seconds) but the progress bar will increase. And I am new to javascript, and the below code also copied from another site , so please help me in adding timer inside the progress bar
Till now I did this code
I want to make like this
Demo
<div class="progress"></div>
<style>
.progress-bar {
height: 20px;
background: #1da1f2;
box-shadow: 2px 14px 15px -7px rgba(30, 166, 250, 0.36);
border-radius: 50px;
transition: all 0.5s;
}
.progress {
width: 100%;
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: start;
background: #e6e9ff;
border-radius: 20px;
box-shadow: 0px 10px 50px #abb7e9;
}
</style>
<script>
/*
* (class)Progress<nowValue, minValue, maxValue>
*/
//helper function-> return <DOMelement>
function elt(type, prop, ...childrens) {
let elem = document.createElement(type);
if (prop) Object.assign(elem, prop);
for (let child of childrens) {
if (typeof child == "string") elem.appendChild(document.createTextNode(child));
else elem.appendChild(elem);
}
return elem;
}
//Progress class
class Progress {
constructor(now, min, max, options) {
this.dom = elt("div", {
className: "progress-bar"
});
this.min = min;
this.max = max;
this.intervalCode = 0;
this.now = now;
this.syncState();
if(options.parent){
document.querySelector(options.parent).appendChild(this.dom);
}
else document.body.appendChild(this.dom)
}
syncState() {
this.dom.style.width = this.now + "%";
}
startTo(step, time) {
if (this.intervalCode !== 0) return;
this.intervalCode = setInterval(() => {
console.log("sss")
if (this.now + step > this.max) {
this.now = this.max;
this.syncState();
clearInterval(this.interval);
this.intervalCode = 0;
return;
}
this.now += step;
this.syncState()
}, time)
}
end() {
this.now = this.max;
clearInterval(this.intervalCode);
this.intervalCode = 0;
this.syncState();
}
}
let pb = new Progress(15, 0, 100, {parent : ".progress"});
//arg1 -> step length
//arg2 -> time(ms)
pb.startTo(5, 500);
//end to progress after 5s
setTimeout( () => {
pb.end()
}, 10000)
</script>
I think the core problem is that the code you copied is overly complicated especially for beginners. What I would recommend is to start from what you know and build up.
Here is the functionality you want written using only core principles of JavaScript and CSS.
let initialTime = 10; //All time in seconds
let timeLeft = initialTime;
let interval;
let progressBarTextElement = document.getElementById('progress-bar-text');
let progressBarElement = document.getElementById('progress-bar');
function render() {
let progressPercentage = (1 - (timeLeft / initialTime) ) * 100;
progressBarElement.style.width = progressPercentage + '%';
progressBarTextElement.innerHTML = timeLeft + 's';
}
function tick() {
timeLeft = timeLeft - 1;
if(timeLeft <= 0) {
clearInterval(interval); //Stops interval
}
render(); //Updates html
}
function startProgressBar() {
interval = setInterval(tick, 1000); //Will call tick every second
render();
}
startProgressBar();
html {font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;}
.progress-bar-continer {
height: 80px;
width: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
background-color: #406086;
}
.progress-bar {
background-color: #1b3e80;
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 0%;
transition: width 1s; /* Makes progressBar smooth */
transition-timing-function: linear; /* Try to remove this line for more tick tock feel */
}
.progress-bar-text {
color: white;
font-size: 24px;
font-weight: 700;
position: relative;
z-index: 1;
}
<div class="progress-bar-continer">
<div class="progress-bar" id="progress-bar"></div>
<div class="progress-bar-text" id="progress-bar-text"></div>
<div>
Try to understand the code best you can and you will have no problems adding any features you want on top of it. All the fancy stuff will come later with experience.

Java Script Sorting algorithm visualizer

k = []
len = 100;
time = true
cont = document.getElementsByClassName("cont")[0];
cont.innerHTML = "";
for (let i = 0; i < len; i++) {
t = Math.round(Math.random() * 800 ) + 5
k.push(t);
cont.innerHTML += "<div class='block' style = 'height:" + t + "px'></div>"
}
function reset(){
k = []
cont.innerHTML = "";
for (let i = 0; i < len; i++) {
t = Math.round(Math.random() * 800 ) + 5
k.push(t);
cont.innerHTML += "<div class='block' style = 'height:" + t + "px'> </div>"
}
}
function bubble(){
function iloop(i){
if(i < len){
setTimeout(function(){
function jloop(j){
if(j < len){
setTimeout(function(){
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
}
cont.innerHTML = "";
for (let p = 0; p < len; p++) {
cont.innerHTML += "<div class='block' style = 'height:" + k[p] + "px'></div>"
}
j++;
jloop(j);
}, 100);
}
}
jloop(0);
i++;
iloop(i);
}, 100);
}
}
iloop(0);
}
.cont {
width: 100%;
height: 900px;
display: block;
background-color: pink;
padding: 0px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center; }
.cont .block {
display: inline-block;
width: 10px;
margin: auto 1px;
background-color: red;
font-size: 5px;
bottom: 0px; }
<button class="reset" onclick="reset()">Reset Array
</button>
<button class="bubble" onclick="bubble()">Bubble Sort
</button>
<div class="cont">
</div>
I am using this simple code to make a javascript visualizer for sorting algorithm but the problem is that it is very choppy and skips multiples frames while running even at a delay of 100ms. I have an i7 7700hq and gtx 1060 so I know that the problem is mostly not my laptop but my approach to it so what approach should I take
Here is a code pen version if your snippets are not working
https://codepen.io/varunagarwal/pen/gOaQqbG
Edit: someone told me to make it a runnable snippet so there you go
You have overlapping setTimeout timers, and a lot of them being scheduled. You only want to yield back to the browser when there's a change to show, and you only want to show a given change once.
Since you're using ES2015+, I'd probably use a generator function to do the sort, yielding when something changes:
function *sortGen() {
for (let i = 0; i < len; ++i) {
for (let j = 0; j < len; ++j) {
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
yield j; // *** Yield to caller, saying what changed
}
}
}
}
Then the code calling it would call its next method, do the update, and then schedule callback for just before the next animation frame via requestAnimationFrame (unless you want to artificially slow it down, in which case setTimeout is fine). Animation frames happen 60 times/second (roughly every 16.666667ms) provided the browser isn't busy doing something else. Here's bubble using the generator from the sortGen function above:
function bubble() {
const gen = sortGen();
tick();
function tick() {
const result = gen.next();
if (!result.done) {
// *** No need to recreate all the elements, just reorder the ones that got swapped
const el = cont.children[result.value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
requestAnimationFrame(tick);
}
}
}
(You could make it an async generator and use a for-await-of loop, but I don't think it really buys you much.)
Here's a live example; I've also included some comments in the code making other suggestions:
"use strict"; // *** Use strict mode
// *** Declare your variables (the old code relied on The Horror of Implicit Globals, which
// strict mode fixes)
let k = []; // *** Consistently use semicolons (or consistently rely on ASI)
let len = 100;
let time = true;
const cont = document.getElementsByClassName("cont")[0];
// *** Don't duplicate code, just use `reset`
reset();
function reset(){
k = [];
// *** Never use += on `innerHTML`
let html = "";
for (let i = 0; i < len; i++) {
// *** Declare your variables
const t = Math.round(Math.random() * 800 ) + 5;
k.push(t);
html += makeBlock(t);
}
cont.innerHTML = html;
}
function makeBlock(value) {
return "<div class='block' style = 'height:" + value + "px'></div>";
}
function *sortGen() {
for (let i = 0; i < len; ++i) {
for (let j = 0; j < len; ++j) {
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
yield j; // *** Yield to caller, saying what changed
}
}
}
}
function bubble() {
const gen = sortGen();
tick();
function tick() {
const result = gen.next();
if (!result.done) {
// *** No need to recreate all the elements, just reorder the ones that got swapped
const el = cont.children[result.value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
requestAnimationFrame(tick);
}
}
}
.cont {
width: 100%;
height: 900px;
display: block;
background-color: pink;
padding: 0px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
} /* *** Don't hide closing } at the end of a line */
.cont .block {
display: inline-block;
width: 10px;
margin: auto 1px;
background-color: red;
font-size: 5px;
bottom: 0px;
} /* *** Don't hide closing } at the end of a line */
<button class="reset" onclick="reset()">Reset Array
</button>
<button class="bubble" onclick="bubble()">Bubble Sort
</button>
<div class="cont">
</div>
Also on CodePen.
For what it's worth, the async generator approach looks something like this:
const nextFrame = cb => new Promise(resolve => {
requestAnimationFrame(() => {
cb();
resolve();
});
});
function bubble() {
(async () => {
for await (const value of sortGen()) {
await nextFrame(() => {
const el = cont.children[value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
});
}
})()
.catch(error => {
// Handle/report error here...
console.error(error);
});
}
"use strict"; // *** Use strict mode
// *** Declare your variables (the old code relied on The Horror of Implicit Globals, which
// strict mode fixes)
let k = []; // *** Consistently use semicolons (or consistently rely on ASI)
let len = 100;
let time = true;
const cont = document.getElementsByClassName("cont")[0];
// *** Don't duplicate code, just use `reset`
reset();
function reset(){
k = [];
// *** Never use += on `innerHTML`
let html = "";
for (let i = 0; i < len; i++) {
// *** Declare your variables
const t = Math.round(Math.random() * 800 ) + 5;
k.push(t);
html += makeBlock(t);
}
cont.innerHTML = html;
}
function makeBlock(value) {
return "<div class='block' style = 'height:" + value + "px'></div>";
}
function *sortGen() {
for (let i = 0; i < len; ++i) {
for (let j = 0; j < len; ++j) {
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
yield j; // *** Yield to caller, saying what changed
}
}
}
}
const nextFrame = cb => new Promise(resolve => {
requestAnimationFrame(() => {
cb();
resolve();
});
});
function bubble() {
(async () => {
for await (const value of sortGen()) {
await nextFrame(() => {
const el = cont.children[value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
});
}
})()
.catch(error => {
// Handle/report error here...
console.error(error);
});
}
.cont {
width: 100%;
height: 900px;
display: block;
background-color: pink;
padding: 0px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
} /* *** Don't hide closing } at the end of a line */
.cont .block {
display: inline-block;
width: 10px;
margin: auto 1px;
background-color: red;
font-size: 5px;
bottom: 0px;
} /* *** Don't hide closing } at the end of a line */
<button class="reset" onclick="reset()">Reset Array
</button>
<button class="bubble" onclick="bubble()">Bubble Sort
</button>
<div class="cont">
</div>
Also on CodePen.

How to add additional times to my counter?

I'm trying to build a countdown clock that goes from one date to the next. An example would be a count down to Halloween then to Thanksgiving. When the countdown reaches zero, I want the counter to restart and count down to the next holiday.
I've tried separating the events and labeling them differently to target them independently. ANy ideas would be great, this has me stuck.
<div class="container">
<p id="timer">
<span id="timer-days"></span>
<span id="timer-hours"></span>
<span id="timer-minutes"></span>
<span id="timer-seconds"></span>
</p>
</div>
<script>
var holidazeEnd=new Date("Jun, 9 2019 18:19:00").getTime();
var holidazeEnd1=new Date("Jun, 9 2019 18:20:00").getTime();
var timer= setInterval(function(){
let now=new Date().getTime();
let t=holidazeEnd - now;
if (t >= 0)
{
let days = Math.floor(t / (1000 * 60 * 60 * 24));
let hours = Math.floor((t % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
let mins = Math.floor((t % (1000 * 60 * 60)) / (1000 * 60));
let secs = Math.floor((t % (1000 * 60)) / 1000);
document.getElementById("timer-days").innerHTML=days+"<span class= 'label'> Days</span>";
document.getElementById("timer-hours").innerHTML=("0"+hours).slice(-2)+"<span class= 'label'> Hours</span>";
document.getElementById("timer-minutes").innerHTML=("0"+mins).slice(-2)+"<span class= 'label'> Minutes</span>";
document.getElementById("timer-seconds").innerHTML=("0"+secs).slice(-2)+"<span class= 'label'> Seconds</span>";
}
else{
document.getElementById("timer").innerHtml=("Happy Independence Day!");}
},1000)
//---------------------------------------------//
var timer1= setInterval(function(){
let now=new Date().getTime();
let t=holidazeEnd1 - now;
if (t >= 0) {
let days1 = Math.floor(t / (1000 * 60 * 60 * 24));
let hours1 = Math.floor((t % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
let mins1 = Math.floor((t % (1000 * 60 * 60)) / (1000 * 60));
let secs1 = Math.floor((t % (1000 * 60)) / 1000);
document.getElementById("timer-days").innerHTML=days1+"<span class= 'label'> Days</span>";
document.getElementById("timer-hours").innerHTML=("0"+hours1).slice(-2)+"<span class= 'label'> Hours</span>";
document.getElementById("timer-minutes").innerHTML=("0"+mins1).slice(-2)+"<span class= 'label'> Minutes</span>";
document.getElementById("timer-seconds").innerHTML=("0"+secs1).slice(-2)+"<span class= 'label'> Seconds</span>";
}
else
document.getElementById("timer1").innerHtml="Merry Xmas!";}
,1000);
countdown(holidazeEnd,timer);
countdown(holidazeEnd1,timer1)
What about to use a single setInterval and an array of holidays?
const holidays = [
{
running: "Independence Day",
complete: "Happy Independence Day!",
time: "July 4 2019"
},
{
running: "Christmas",
complete: "Merry Xmas!",
time: "Dec 10 2019"
}
];
We need to know the next holiday to track:
//Get the next holiday index
const nextHolidayIndex = () => holidays.reduce((prevResult,current, i) => {
const timeDif = Date.parse(current.time) - Date.now();
if(timeDif < 0)
return prevResult;
if(prevResult.index == -1 || timeDif < prevResult.diff) return {
index : i,
diff: timeDif
};
return prevResult;
}, {index : -1, diff: 0}).index;
The time remaining, like you did:
//Get time remaining for a given time
const getTimeRemaining = (timeTime) =>{
const t = Date.parse(timeTime) - Date.now();
const seconds = Math.floor( (t/1000) % 60 );
const minutes = Math.floor( (t/1000/60) % 60 );
const hours = Math.floor( (t/(1000*60*60)) % 24 );
const days = Math.floor( t/(1000*60*60*24) );
return {
'total': t,
'days': days,
'hours': hours,
'minutes': minutes,
'seconds': seconds
};
}
And we need to initialize the component:
const initializeClock = (id, nextHoliday) => {
const clock = document.getElementById(id);
const message = clock.querySelector('.message');
const daysSpan = clock.querySelector('.days');
const hoursSpan = clock.querySelector('.hours');
const minutesSpan = clock.querySelector('.minutes');
const secondsSpan = clock.querySelector('.seconds');
let interval
function updateClock() {
const t = getTimeRemaining(nextHoliday.time);
daysSpan.innerHTML = t.days.toString();
hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
if (t.total <= 0) {
clearInterval(interval);
message.innerHTML = nextHoliday.complete
}
}
message.innerHTML = `${nextHoliday.running}`
updateClock();
interval = setInterval(updateClock, 1000);
}
How about the html ?
<div id="timer">
<h1 class="message"></h1>
<div class="countdown">
<div>
<span class="days"></span>
<div class="smalltext">Days</div>
</div>
<div>
<span class="hours"></span>
<div class="smalltext">Hours</div>
</div>
<div>
<span class="minutes"></span>
<div class="smalltext">Minutes</div>
</div>
<div>
<span class="seconds"></span>
<div class="smalltext">Seconds</div>
</div>
</div>
</div>
Putting some visual to it:
#timer{
text-align: center;
background: #efefef;
font-family: sans-serif;
font-weight: 100;
display: inline-flex;
flex-direction: column;
padding: 24px;
}
h1{
color: #969696;
font-weight: 100;
font-size: 40px;
margin: 0 0 15px 0;
}
#timer .countdown{
color: #fff;
display: inline-block;
font-weight: 100;
text-align: center;
font-size: 30px;
}
#timer .countdown > div{
padding: 10px;
border-radius: 3px;
background: #6f7b75;
display: inline-block;
}
#timer .countdown div > span{
padding: 15px;
border-radius: 3px;
background: #03A9F4;
display: inline-block;
}
.smalltext{
padding-top: 5px;
font-size: 16px;
}
Then we just run the code:
const next = nextHolidayIndex()
if(next != -1){
initializeClock('timer', holidays[next]);
}
Here you go :)
const holidays = [
{
running: "Independence Day",
complete: "Happy Independence Day!",
time: "July 4 2019"
},
{
running: "Christmas",
complete: "Merry Xmas!",
time: "Dec 10 2019"
}
];
//Get the next holiday index
const nextHolidayIndex = () => holidays.reduce((prevResult,current, i) => {
const timeDif = Date.parse(current.time) - Date.now();
if(timeDif < 0)
return prevResult;
if(prevResult.index == -1 || timeDif < prevResult.diff) return {
index : i,
diff: timeDif
};
return prevResult;
}, {index : -1, diff: 0}).index;
//Get time remaining for a given time
const getTimeRemaining = (timeTime) =>{
const t = Date.parse(timeTime) - Date.now();
const seconds = Math.floor( (t/1000) % 60 );
const minutes = Math.floor( (t/1000/60) % 60 );
const hours = Math.floor( (t/(1000*60*60)) % 24 );
const days = Math.floor( t/(1000*60*60*24) );
return {
'total': t,
'days': days,
'hours': hours,
'minutes': minutes,
'seconds': seconds
};
}
const initializeClock = (id, nextHoliday) => {
const clock = document.getElementById(id);
const message = clock.querySelector('.message');
const daysSpan = clock.querySelector('.days');
const hoursSpan = clock.querySelector('.hours');
const minutesSpan = clock.querySelector('.minutes');
const secondsSpan = clock.querySelector('.seconds');
let interval
function updateClock() {
const t = getTimeRemaining(nextHoliday.time);
daysSpan.innerHTML = t.days.toString();
hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
if (t.total <= 0) {
clearInterval(interval);
message.innerHTML = nextHoliday.complete
startNext()
}
}
message.innerHTML = `${nextHoliday.running}`
updateClock();
interval = setInterval(updateClock, 1000);
}
const startNext = () => {
const next = nextHolidayIndex()
if(next != -1){
initializeClock('timer', holidays[next]);
}
}
startNext()
#timer{
text-align: center;
background: #efefef;
font-family: sans-serif;
font-weight: 100;
display: inline-flex;
flex-direction: column;
padding: 24px;
}
h1{
color: #969696;
font-weight: 100;
font-size: 40px;
margin: 0 0 15px 0;
}
#timer .countdown{
color: #fff;
display: inline-block;
font-weight: 100;
text-align: center;
font-size: 30px;
}
#timer .countdown > div{
padding: 10px;
border-radius: 3px;
background: #6f7b75;
display: inline-block;
}
#timer .countdown div > span{
padding: 15px;
border-radius: 3px;
background: #03A9F4;
display: inline-block;
}
.smalltext{
padding-top: 5px;
font-size: 16px;
}
<div id="timer">
<h1 class="message"></h1>
<div class="countdown">
<div>
<span class="days"></span>
<div class="smalltext">Days</div>
</div>
<div>
<span class="hours"></span>
<div class="smalltext">Hours</div>
</div>
<div>
<span class="minutes"></span>
<div class="smalltext">Minutes</div>
</div>
<div>
<span class="seconds"></span>
<div class="smalltext">Seconds</div>
</div>
</div>
</div>
In order to restart the counter we need to refresh the page. If you don't want it then, forget about the message, and just init the next after the previous reach 0.
const initializeClock = (id, nextHoliday) => {
//...
if (t.total <= 0) {
clearInterval(interval);
startNext()
}
//...
}
const startNext = () => {
const next = nextHolidayIndex()
if(next != -1){
initializeClock('timer', holidays[next]);
}
}
startNext()
I think this example goes around the question:
- you can add as many dates as you want,
- adjust the end date of their presentation before moving on to the next countdown.
I do some changing for cosmetic reasons...
// Set all the dates you want (no order needed)
const holidays = [
{ dat: 'July, 3 YYYY 23:59:59', end: 'July, 4 YYYY 23:59:59', msg:'Happy Independence Day!', bef:'Independence Day', Bcolor : '#39CCCC' }
, { dat: 'Dec, 24 YYYY 23:59:59', end: 'Dec, 26 YYYY 12:00:00', msg:'Merry Xmas!', bef:'Xmas', Bcolor : '#2ECC40' }
, { dat: 'Oct, 31 YYYY 17:59:59', end: 'Nov, 1 YYYY 06:00:00', msg:'Happy Halloween!', bef:'Halloween', Bcolor : '#F012BE' }
, { dat: 'Dec, 31 YYYY 23:59:59', end: 'Jan, 1 Y+1Y 01:00:00', msg:'Happy New Year!', bef:'New Year', Bcolor : '#FFDC00' }
];
// please note the use of 'YYYY' stand for 'any Year' and 'Y+1Y' stand fort 'any Following Year' for events repeating each year.
// Real year values are ok but old events will be ignored
// The color is here for example, you can disable it or change the idea by placing a picture in the background,
// and do the same countdown for each species
// Ready for everything:
const BODY_color = '#554a63' // standard Color of Background
, timer_on = document.querySelector('#timer-on')
, timer_off = document.querySelector('#timer-off')
, curDT = document.getElementById('currentDateTime')
, tCountDown = document.querySelectorAll('#countdown div span')
, xDays = 0
, xHours = 2
, xMins = 4
, xSecs = 6
, t_msg = document.querySelector('#timer-on h2')
, H_message = document.querySelector('#timer-off h1')
, one_Sec = 1000
, one_Min = one_Sec * 60
, one_Hour = one_Min * 60
, one_Day = one_Hour * 24
, Option_DT = { weekday: 'long', month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit'}
, Intl_F_DT = new Intl.DateTimeFormat('en-US', Option_DT )
;
/* Search function to set the countDown target
do : set interface to correct closest countdown
even if it is during a period before ending event
return : (js object )
start : time target of event
ending : time target of event ending
complete : Boolean value (set to false) according to the countdown is not completed
Bcolor : paint color value for background during ending period
*/
function SetNextHolidayTarget()
{
let now = new Date()
, nowTime = now.getTime()
, thisYear = now.getFullYear()
, closest = one_Day *365 /// start with big closest date
, target = null
;
// default display
timer_on.classList.remove('noDisplay')
timer_off.classList.add('noDisplay')
document.body.style.backgroundColor = BODY_color;
holidays.forEach(H=> // search loop for the closest date in 'holidays'
{
let s_date = H.dat.replace('YYYY', thisYear ) // change 'YYYY' string to current year value
, e_date = H.end.replace('YYYY', thisYear ).replace('Y+1Y', (thisYear +1) )
, t_date = new Date(s_date) // target date
, z_date = new Date(e_date) // target date ending
;
if ( t_date < now && z_date < now ) // if target date and ending are before today, this set them for next year
{
s_date = H.dat.replace('YYYY', (thisYear +1))
e_date = H.end.replace('YYYY', (thisYear +1)).replace('Y+1Y', (thisYear +2) )
t_date = new Date(s_date)
z_date = new Date(e_date)
}
let t_datesDiff = t_date.getTime() - nowTime
, z_datesDiff = z_date.getTime() - nowTime
if ( closest > t_datesDiff && t_datesDiff > 0 ) // if it is closer than the dates already seen
{
closest = t_datesDiff
t_msg.textContent = H.bef
H_message.textContent = H.msg
target = { start: t_date.getTime(), ending: z_date.getTime(), complete: false, Bcolor: H.Bcolor }
}
if ( closest > z_datesDiff && z_datesDiff > 0 ) // case of inside countdown period
{
closest = z_datesDiff
t_msg.textContent = H.bef
H_message.textContent = H.msg
target = { start: z_date.getTime(), ending: z_date.getTime(), complete: true, Bcolor: H.Bcolor }
}
})
if ( target.complete ) // if inside countdown period
{
timer_on.classList.add('noDisplay')
timer_off.classList.remove('noDisplay')
document.body.style.backgroundColor = target.Bcolor;
}
return target
}
// **** INIT ****
var holidazeEnd = SetNextHolidayTarget()
// first TEST to prove -------------------------------------------------------------------
if (true) // (set to false to remove this test)
{
let now = new Date()
, start = new Date()
, ending = new Date()
;
// default display
timer_on.classList.remove('noDisplay')
timer_off.classList.add('noDisplay')
document.body.style.backgroundColor = BODY_color;
start.setTime(now.getTime() + (one_Sec *20) );
ending.setTime(start.getTime() + (one_Sec *15) );
t_msg.textContent = 'Test to 20s'
H_message.textContent = 'happy test ! (15s)'
holidazeEnd = { start , ending , complete: false, Bcolor: 'orange' }
} // =====================================================================================
// TIMER Interval //
// - - - - - - - - //
var timerInterval = setInterval(function ()
{
let now = new Date()
, t = holidazeEnd.start - now.getTime()
;
curDT.textContent = Intl_F_DT.format(now) // show date time upper right
if (t >= 0 )
{
if (!holidazeEnd.complete)
{
tCountDown[xDays].textContent = Math.floor(t / one_Day);
tCountDown[xHours].textContent = ('0' + Math.floor((t % one_Day) / one_Hour)).slice(-2);
tCountDown[xMins].textContent = ('0' + Math.floor((t % one_Hour) / one_Min)).slice(-2);
tCountDown[xSecs].textContent = ('0' + Math.floor((t % one_Min) / one_Sec)).slice(-2);
}
// else { nothing to do }
}
else // elapsed time !
{
if ( holidazeEnd.complete ) // elapsed time of ' holiday ' message
{
holidazeEnd = SetNextHolidayTarget() // searching the next one in the ' holidays ' list
}
else // show ' holiday ' message
{
timer_on.classList.add('noDisplay')
timer_off.classList.remove('noDisplay')
holidazeEnd.complete = true;
holidazeEnd.start = holidazeEnd.ending
document.body.style.backgroundColor = holidazeEnd.Bcolor
}
}
}, 1000);
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
background-color: #554a63;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
#currentDateTime {
text-align: right;
padding: .2em 1em 0 0;
color: white;
font-weight: lighter;
font-stretch: expanded
}
.noDisplay {
display:none !important
}
.timer{
position: absolute;
top: 50%;
left: 0;
display: block;
width: 100%;
transform: translateY(-50%);
}
.timer h1 {
text-align: center;
font-size: 4em;
font-weight: bold;
color: white;
background-color: rgba(96, 87, 151, 0.315);
padding: .8em 0;
}
.timer h2 {
text-align: center;
padding: .3em 0;
}
#countdown {
display: table;
margin: 0 auto;
padding: 1em 0;
background-color: rgba(96, 87, 151, 0.315);
min-width: 26em;
}
#countdown div {
display: block;
float: left;
height: 5em;
width: 6em;
border-right: 1px solid grey;
}
#countdown div:first-child {
width: 8em;
}
#countdown div:last-child {
border-right: none;
}
#countdown div span {
display: inline-block;
width: 100%;
text-align: center;
color: white
}
#countdown div span:first-child {
font-size: 3em;
}
#countdown div span:last-child {
font-size: .8em;
padding: 1em .2em;
}
<p id="currentDateTime"></p>
<div id="timer-on" class="timer">
<h2> .. </h2>
<div id="countdown">
<div><span>.</span><span>Days</span></div>
<div><span>.</span><span>Hours</span></div>
<div><span>.</span><span>Minutes</span></div>
<div><span>.</span><span>Seconds</span></div>
</div>
</div>
<div id="timer-off" class="timer noDisplay">
<h1>!</h1>
</div>

javascript: get random pairs of an array without duplicates

I have an array of students, that should be random paired by clicking the button "button-newPairs". These pairs should be shown in 8 divs "pair one", "pair two", ... that contains two spans "studentOne" and "studentTwo".
I get the pairs in console but not by clicking the button "button-newPairs" and I donĀ“t know how to change or insert the text content in my spans. Can someone help me, please? Thank you in advance.
var students = ['Al', 'Ma', 'Pu', 'Mi', 'Ma', 'Me', 'Ca', 'Na', 'Ja', 'Go', 'Ha', 'Fa', 'Ti', 'Fi' ];
var studentOne = document.querySelector('#student1');
var studentTwo = document.querySelector('#student2');
if (students.length % 2 != 0) {
alert("You must have an even number of students. You currently have " + students.length + " students.");
} else {
var arr1 = students.slice(),
arr2 = students.slice();
arr1.sort(function () { return 0.5 - Math.random(); });
arr2.sort(function () { return 0.5 - Math.random(); });
while (arr1.length) {
var student1 = arr1.pop(),
student2 = arr2[0] == student1 ? arr2.pop() : arr2.shift();
$(".button-newPairs").click(function () {
studentOne.textContent = student1;
studentTwo.textContent = student2;
});
console.log(student1 + ' works with ' + student2);
}
}
.container-pairs {
display: grid;
grid-template-columns: 150px 150px;
grid-gap: 20px;
justify-content: space-around;
align-content: center;
margin-bottom:20px;
}
.one {
grid-column: 1 / 2;
grid-row: 1;
}
.two {
grid-column: 2 / 2;
grid-row: 1;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="container-pairs">
<div class="pair one">
<span id="studentOne">NEW </span> <br>
<span id="studentTwo"> PAIRS</span>
</div>
<div class="pair two">
<span id="studentOne">NEW </span><br>
<span id="studentTwo"> PAIRS</span>
</div>
<div id="container-footer">
<div class="button-newPairs">
<span>NEW PAIRS</span>
</div>
</div>
</body>
I've commented inline. Look for the lines with a //
// Let's wrap this in a function so that we can call it with our button.
function makePairs() {
// We will clear our lists before each run of the function.
$('#studentOne').html('<h1>Student 1</h1>');
$('#studentTwo').html('<h1>Student 2</h1>');
var students = ['Al', 'Ma', 'Pu', 'Mi', 'Ma', 'Me', 'Ca', 'Na', 'Ja', 'Go', 'Ha', 'Fa', 'Ti', 'Fi'];
// By capturing these nodes in variables, we can reference them as our targets for insertion, below.
var studentOne = document.querySelector('#studentOne');
var studentTwo = document.querySelector('#studentTwo');
if (students.length % 2 != 0) {
alert("You must have an even number of students. You currently have " + students.length + " students.");
} else {
var arr1 = students.slice(),
arr2 = students.slice();
arr1.sort(function() {
return 0.5 - Math.random();
});
arr2.sort(function() {
return 0.5 - Math.random();
});
// Here we make a function that will insert a DOM fragment inside a target node
const insertFragment = (output, target) => {
let el;
let fragment = document.createDocumentFragment();
el = document.createElement('p');
el.innerText = output
fragment.appendChild(el);
target.appendChild(fragment);
}
while (arr1.length) {
var student1 = arr1.pop(),
student2 = arr2[0] == student1 ? arr2.pop() : arr2.shift();
// Here we use the function, sending student1 (the student) to studentOne (the target DOM node specified at the very top, #studentOne)
insertFragment(student1, studentOne)
insertFragment(student2, studentTwo)
console.log(student1 + ' works with ' + student2);
}
}
}
// Run the function on load
makePairs();
.container-pairs {
display: grid;
grid-template-columns: 150px 150px;
grid-gap: 20px;
justify-content: space-around;
align-content: center;
margin-bottom: 20px;
}
.one {
grid-column: 1 / 2;
grid-row: 1;
}
.two {
grid-column: 2 / 2;
grid-row: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="container-pairs">
<div id="studentOne">
<h1>Student 1</h1>
</div>
<div id="studentTwo">
<h1>Student 2</h1>
</div>
</div>
<div id="container-footer">
<button class="button-newPairs" onclick="makePairs()">
NEW PAIRS
</button>
</div>
</body>
The button tag, just above this line, has registered an event handler for clicks that will run the makePairs() function again.
I would use splice to remove the user from the array and loop through each user like so:
let students = ['Al', 'Ma', 'Pu', 'Mi', 'Ma', 'Me', 'Ca', 'Na', 'Ja', 'Go', 'Ha', 'Fa', 'Ti', 'Fi'];
while (students.length) {
let student1 = students.splice((Math.random() * 1000) % students.length, 1)
let student2 = students.splice((Math.random() * 1000) % students.length, 1)
console.log(`${student1} works with ${student2}`)
}

Categories

Resources