I need help on the script for the counter to increment by one and have a animation of the incremented number, slide to top
something like that on the picture and this is what I have done so far.
.booking-wrapper .countdown-wrapper .countdown-container .order-list {
border: 5px solid #959595;
list-style-type: none;
padding-left: 0;
margin-bottom: 0;
overflow: hidden;
line-height: 1;
height: 56px;
margin-top: 8px;
}
.booking-wrapper .countdown-wrapper .countdown-container .order-list .order-list-item {
display: inline;
font-size: 40px;
font-weight: bold;
padding: 0 15px;
}
<div class="countdown-container">
<ul id="order-list" class="order-list">
<li class="order-list-item"><span>0</span></li>
<li class="order-list-item" style="border-left: 1px solid black;"><span>0</span></li>
<li class="order-list-item" style="border-left: 1px solid black; border-right: 1px solid black;"><span>8</span></li>
<li class="order-list-item"><span>1</span></li>
</ul>
</div>
You should probably use some existing library for that. But if you want to have your own implementation, then use the following elements:
Use CSS transitions, and in particular the transition property and the transitionend event.
Build the inner HTML dynamically, so that in the original HTML you only have the container element, which will initially be empty.
Let the HTML at the deepest level (for one digit) be 3 lines: on each line you put a digit. For example 8<br>9<br>0. They should form a sequence. Scroll the middle digit into view so that only that digit is visible. Perform the animation upward or downward as needed, and when the animation is complete, update the HTML and reset the scroll offset (which can be the top CSS property).
There is no need to have the span elements. Just put the content straight into the li elements.
Use promises, which make asynchronous code look better, certainly when using await
So here is how you could do it:
// Utility functions returning promises
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const nextFrame = () => new Promise(resolve => requestAnimationFrame(resolve));
const animate = (elem, prop, value, duration) => {
return nextFrame().then(() => new Promise(resolve => {
elem.style.transition = `${prop} ${duration}ms`;
elem.style[prop] = `${value}px`;
const done = () => {
elem.style.transition = `${prop} 0ms`;
resolve();
}
elem.addEventListener("transitionend", done, {once: true});
})).then(nextFrame);
};
// DOM element wrapper for the counter functionality
class Counter {
constructor(element, length = 4, upwards = true) {
this.element = element;
this._value = 0;
this.upwards = !!upwards;
this.digits = Array.from({length}, () => element.appendChild(document.createElement("li")));
}
get value() {
return this._value;
}
set value(value) {
this._value = value;
const numStr = value.toString().padStart(4, "0").slice(-this.digits.length);
// Display the current number in the counter element (no animation)
this.digits.forEach( (digit, i) => {
// Put three lines, each having a digit, where the middle one is the current one:
digit.innerHTML = `${(+numStr[i]+(this.upwards ? 9 : 1))%10}<br>${numStr[i]}<br>${(+numStr[i]+(this.upwards ? 1 : 9))%10}`;
digit.style.top = `${-this.element.clientHeight}px`; // scroll the middle digit into view
});
}
async roll(direction = 1, duration = 500) {
await nextFrame();
const numChangingDigits = Math.min(this.digits.length,
this.value.toString().length - this.value.toString().search(direction > 0 ? /9*$/ : /0*$/) + 1);
const numStr = this.value.toString().padStart(4, "0").slice(-numChangingDigits);
const promises = this.digits.slice(-numChangingDigits).map((digit, i) =>
animate(digit, "top", (direction > 0) === this.upwards ? -this.element.clientHeight*2 : 0, duration)
);
await Promise.all(promises);
this.value = this.value + direction;
await nextFrame();
}
async rollTo(target, duration = 500, pause = 300) {
const direction = Math.sign(target - this.value);
while (this.value !== target) {
await this.roll(direction, duration);
await delay(pause);
}
}
}
// Demo:
const counter = new Counter(document.getElementById("order-list"), 4, true);
counter.value = 9931;
counter.rollTo(10002, 500, 300);
.order-list {
border: 5px solid #999;
list-style-type: none;
padding-left: 0;
overflow: hidden;
height: 50px;
line-height: 1;
display: inline-block;
}
.order-list > li {
display: inline-block;
font-size: 50px;
font-weight: bold;
padding: 0 15px;
border-left: 1px solid black;
position: relative;
top: 0;
}
.order-list > li:first-child {
border-left: 0;
}
<ul id="order-list" class="order-list"></ul>
There are some arguments you can use to modify the speed of the animations. There is also an argument that determines whether the dials roll upward or downward for getting the next digit.
Related
We have the usual example code where everything works.
(myFunction gets triggered on an onclick event.)
"use strict";
console.clear();
const obj = {
name: "falana",
count: 0
};
function myFunction() {
obj.count++;
console.log(obj.count);
console.log(obj.name);
console.log(obj);
}
---Output---
1
"falana"
// [object Object]
{
"name": "falana",
"count": 1
}
From this example, we can access a global object obj inside another function (in this case, myFunction) without any ReferenceError.
I was trying to create a single page Twitter clone (only DOM manipulation) and kept getting this error.
Uncaught ReferenceError: Cannot access 'userData' before initialization at postMessage
---Javascript that's causing error---
window.onload = function() {
console.clear();
}
const userData = {
username: 'Chinmay Ghule',
userhandle: generateUserhandle(this.username),
userPostCount: 0
};
function generateUserhandle(userData) {
const usernameArr = userData.username.split(" ");
usernameArr.forEach(element => {
element.toLowerCase();
});
return "#" + usernameArr.join("");
}
// posts message entered in #message-text to
// message-output-container.
function postMessage() {
console.log(userData);
// get message from #message-text.
const message = document.getElementById('message-text').value;
console.log(`message: ${message}`);
// check for length.
console.log(`message length: ${message.length}`);
if (message.length === 0) {
return;
}
// create new div.
const card = document.createElement('div');
const userInfo = document.createElement('div');
const userMessage = document.createElement('div');
const usernameSpan = document.createElement('span');
const userhandleSpan = document.createElement('span');
const beforeTimeDotSpan = document.createElement('span');
const timeSpan = document.createElement('span');
usernameSpan.classList.add('username');
userhandleSpan.classList.add('userhandle');
beforeTimeDotSpan.classList.add('before-time-dot');
timeSpan.classList.add('time');
userInfo.appendChild(usernameSpan);
userInfo.appendChild(userhandleSpan);
userInfo.appendChild(beforeTimeDotSpan);
userInfo.appendChild(timeSpan);
console.log(`userInfo : ${userInfo}`);
userInfo.classList.add('user-info');
userMessage.classList.add('output-message');
card.appendChild(userInfo);
card.appendChild(userMessage);
console.log(`card : ${card}`);
card.classList.add('output-message');
userMessage.innerText = message;
// check for number of posts.
if (userData.userPostCount === 0) {
let noMessageDiv = document.getElementById("no-message-display");
noMessageDiv.remove();
}
// append new div.
const messageOutputContainer = document.getElementById('message-output-container');
messageOutputContainer.appendChild(card);
// increment userPostCount.
userData.userPostCount++;
}
Why am i getting this ReferenceError in this case, while it didn't in our first example code?
Your code had quite some issues...
The biggest change here was getting rid of generateUserhandle entirely and making it with a getter.
get userhandle() {
return this.username.toLowerCase()
},
Working demo
window.onload = function() {
console.clear();
}
const userData = {
username: 'Chinmay Ghule',
get userhandle() {
return this.username.toLowerCase()
},
userPostCount: 0
};
// posts message entered in #message-text to
// message-output-container.
function postMessage() {
console.log(userData);
// get message from #message-text.
const message = document.getElementById('message-text').value;
console.log(`message: ${message}`);
// check for length.
console.log(`message length: ${message.length}`);
if (message.length === 0) {
return;
}
// create new div.
const card = document.createElement('div');
const userInfo = document.createElement('div');
const userMessage = document.createElement('div');
const usernameSpan = document.createElement('span');
const userhandleSpan = document.createElement('span');
const beforeTimeDotSpan = document.createElement('span');
const timeSpan = document.createElement('span');
usernameSpan.classList.add('username');
userhandleSpan.classList.add('userhandle');
beforeTimeDotSpan.classList.add('before-time-dot');
timeSpan.classList.add('time');
userInfo.appendChild(usernameSpan);
userInfo.appendChild(userhandleSpan);
userInfo.appendChild(beforeTimeDotSpan);
userInfo.appendChild(timeSpan);
console.log(`userInfo : ${userInfo}`);
userInfo.classList.add('user-info');
userMessage.classList.add('output-message');
card.appendChild(userInfo);
card.appendChild(userMessage);
console.log(`card : ${card}`);
card.classList.add('output-message');
userMessage.innerText = message;
// check for number of posts.
if (userData.userPostCount === 0) {
let noMessageDiv = document.getElementById("no-message-display");
noMessageDiv.remove();
}
// append new div.
const messageOutputContainer = document.getElementById('message-output-container');
messageOutputContainer.appendChild(card);
// increment userPostCount.
userData.userPostCount++;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0px;
padding: 0px;
font-family: Arial, Helvetica, sans-serif;
}
#container {
/*background-color: lightskyblue;*/
margin: 2.5rem 25%;
}
#user-info {
/*background-color: orange;*/
padding-bottom: 0.5rem;
}
.username {
font-weight: bold;
}
.userhandle,
.before-time-dot,
.time {
opacity: 0.75;
}
#message-text {
width: 100%;
margin-bottom: 0.5rem;
font-size: 18px;
padding: 0.5rem;
resize: none;
border-radius: 0.5rem;
outline: 1px solid lightgray;
}
#message-button {
float: right;
padding: 0.375rem 1.5rem;
font-weight: bold;
font-size: 18px;
border-radius: 1rem;
}
#message-button:hover {
background-color: lightgray;
}
#message-input-container {
background-color: lightskyblue;
padding: 1rem;
border-radius: 0.5rem;
}
#message-input-container::after {
content: "";
clear: both;
display: table;
}
#message-output-container {
/*background-color: lightskyblue;*/
margin-top: 30px;
border-top: 3px solid black;
}
#no-message-display {
text-align: center;
padding: 0.5rem;
}
.output-message {
padding: 0.5rem;
font-size: 18px;
border-bottom: 1px solid lightgray;
}
<div id="container">
<div id="message-input-container">
<textarea id="message-text" rows="5" placeholder="Type your message here..." contenteditable="" value=""></textarea>
<input id="message-button" type="button" value="Post" onclick="postMessage();" />
</div>
<div id="message-output-container">
<div id="no-message-display">
<span>No messages yet!</span>
</div>
</div>
</div>
I have a div which is displayed with onclick then which disappear with setTimeout:
css
#light {
position:absolute;
left:40px;
top:45px;
border-left:50px solid transparent;
border-right:50px solid transparent;
border-bottom:20px solid red;
opacity:0;
}
js (don't know if the syntax is correct but it works)
function change() {
var element = document.getElementById("light");
element.style.opacity = "1";
element.style.transitionDelay = "4s", // only the 1stime with onclick
setTimeout(() => {
element.style.opacity = "0";
}, 5000)
}
html
<button onclick="change()">light</button>
<div id="light"></div>
What I would like is that this action repeats then by itself every 2 minutes:
- after 2min, #light is displayed again for 5s (opacity="1")
- then hidden again (opacity="0")and so on, and so every 2 min.I know about the setInterval() method but it's too difficult for me to use it correctly - the script doesn't work at regular interval (every 2 min).ps: I've looked similar questions but all this is beyond my competence (ie, 0).
I'm not sure why you can't use timeouts?
Wouldn't something like this work?
( you can adjust the timers... I didn't want to wait minutes to see the light blink so I set it to few seconds)
let timer;
let started = false;
let delayTimer;
const lightOn = (clicked) => {
// do nothing if clicked for the second time
if (clicked && started) {return;}
const fn = () => {
const element = document.getElementById("light");
element.classList.add('light-on');
timer = setTimeout(lightOff, 1000);
};
if (clicked) {
delayTimer = setTimeout(fn, 3000);
} else {
fn();
}
started = true;
}
const lightOff = () => {
const element = document.getElementById("light");
element.classList.remove('light-on');
timer = setTimeout(lightOn, 2000);
}
const stop = () => {
clearTimeout(timer);
clearTimeout(delayTimer);
timer = undefined;
delayTimer = undefined;
started = false;
}
.light {
background-color: gray;
opacity: 0.1;
width: 2em;
height: 2em;
display: inline-block;
border: 1px solid black;
border-radius: 2em;
}
.light-on {
background-color: yellow;
opacity: 1;
}
<div id="light" class="light"></div>
<button onclick="lightOn(true)" style="display: block;">start</button>
<button onclick="stop()" style="display: block;">stop</button>
This code fetches multiple words from the same arrays of words and parse it into HTML. Inside the get_word function, how can I avoid the same word from being selected and printed multiple times?
NO: Car, Car, House, Cat
YES: Car, Dog, House, Cat
var words = ["Buss", "Plane", "Car","Dog","Cat", "House"];
get_random = function (list) {
return list[Math.floor((Math.random()*list.length))];
}
get_word = function (number) {
for (i = 1; i < number+1; i++) {
word = get_random(words);
document.getElementById("word"+i).innerHTML = word;
}
}
start = function () {
get_word(3);
}
div.buttons {
text-align: center;
margin: 15px;
}
.button {
background-color: #4CAF50;
/* Green */
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
#word1,
#word2,
#word3,
#word4 {
text-align: center;
font-size: 48px;
color: red;
bottom: 15px;
}
<div id="word1"></div>
<div id="word2"></div>
<div id="word3"></div>
<div id="word4"></div>
<div class="buttons">
<button class="button" onclick="start();">Try it</button>
</div>
I aggree with hoangdv but it can be event simpler
const words = ["Bus", "Plane", "Car","Dog","Cat", "House"];
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
return array
}
get_word = function (number) {
return shuffle(words).slice(0,number)
}
console.log(get_word(3))
console.log(get_word(3))
console.log(get_word(3))
I think the solution to your problem is using pop(), although you will need to make sure that you keep a copy of your original list somewhere if you need to reference it later, as pop will change the list and reduce its length.
This means your function get_random should look closer to:
get_random = function (list)
{
return list.pop(Math.floor((Math.random()*list.length)))
}
I think you can shuffle the array then get get each item of the the shuffled array.
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}
get_word = function (number) {
shuffle(words);
for (i = 1; i < number+1; i++) {
word = words[i - 1];
document.getElementById("word"+i).innerHTML = word;
}
}
Add an extra variable can resolve the issue.
get_word = function (number) {
var out = [];
for (i = 1; i < number+1; i++) {
word = get_random(words);
if(out.findIndex(w=>w==word) > -1){
out.push(word);
document.getElementById("word"+i).innerHTML = word;
}
}
}
Been trying to add a typewriter effect, it works great on codepen but not in WP. I've tried to add the code directly to and also to load it as a .js file from the theme's folder, I can see in page-source it's loaded, but the effect isn't executing.
I've checked in console and there's no errors, initially I got an "Uncaught TypeError: Cannot read property 'innerHTML'", so I moved it to my footer.
This is my code:
HTML
<div>
<span id="container">Testing </span> <span id="text"></span><div id="cursor"></div>
</div>
CSS
#container {
display: inline;
vertical-align: middle;
font-family: 'Poppins',Helvetica,Arial,Lucida,sans-serif!important;
font-weight: 500!important;
font-size: 45px!important;
color: #000000!important;
text-align: left!important;
line-height: 1.5em;
}
#text {
display: inline;
vertical-align: middle;
font-family: 'Poppins',Helvetica,Arial,Lucida,sans-serif!important;
font-weight: 500!important;
font-size: 45px!important;
color: #000000!important;
text-align: left!important;
height: 70px;
line-height: 1.5em;
}
#cursor {
display: inline-block;
vertical-align: middle;
width: 3px;
height: 50px;
background-color: #000000;
animation: blink .75s step-end infinite;
}
#keyframes blink {
from, to {
background-color: transparent
}
50% {
background-color: #000000;
}
}
JS
// List of sentences
var _CONTENT = [
"This",
"That",
"These",
"Those"
];
// Current sentence being processed
var _PART = 0;
// Character number of the current sentence being processed
var _PART_INDEX = 0;
// Holds the handle returned from setInterval
var _INTERVAL_VAL;
// Element that holds the text
var _ELEMENT = document.querySelector("#text");
// Cursor element
var _CURSOR = document.querySelector("#cursor");
// Implements typing effect
function Type() {
// Get substring with 1 characater added
var text = _CONTENT[_PART].substring(0, _PART_INDEX + 1);
_ELEMENT.innerHTML = text;
_PART_INDEX++;
// If full sentence has been displayed then start to delete the sentence after some time
if(text === _CONTENT[_PART]) {
// Hide the cursor
_CURSOR.style.display = 'none';
clearInterval(_INTERVAL_VAL);
setTimeout(function() {
_INTERVAL_VAL = setInterval(Delete, 50);
}, 1000);
}
}
// Implements deleting effect
function Delete() {
// Get substring with 1 characater deleted
var text = _CONTENT[_PART].substring(0, _PART_INDEX - 1);
_ELEMENT.innerHTML = text;
_PART_INDEX--;
// If sentence has been deleted then start to display the next sentence
if(text === '') {
clearInterval(_INTERVAL_VAL);
// If current sentence was last then display the first one, else move to the next
if(_PART == (_CONTENT.length - 1))
_PART = 0;
else
_PART++;
_PART_INDEX = 0;
// Start to display the next sentence after some time
setTimeout(function() {
_CURSOR.style.display = 'inline-block';
_INTERVAL_VAL = setInterval(Type, 100);
}, 200);
}
}
// Start the typing effect on load
_INTERVAL_VAL = setInterval(Type, 100);
I am looking for ideas for pagination alternatives. I am aware of 2 pagination schemes:
Click on pages pagination - my favorite example
Infinite scroll pagination - one implementation here that seems to work
There must be some other less known/popular ways to do it. Bonus points if you can provide a link to a demo
Thanks
I think that a good alternative to paging is a way, or more than one way, for the user to tell the server something about what it is they're looking for. For some types of content (like, a whole lot of text, say from a research paper or a work of fiction), of course you're probably stuck with paging. But when the content is naturally searchable (like tables of checking account transactions), good, simple filtering tools are probably more useful than pagination schemes. (Actually you may need both.)
I worked on a GWT hybrid technique where it did an "infinite scroll" but only showed a "window/page" of information at a time. So it only loaded a fixed amount of data to the browser at any given time. If you could display 20 items and scrolled it just updated the list 20 items at a time. Paging without paging, scrolling without scrolling.
Of course this is a trivial definition of the actual implementation, which was much smarter than this sounds and very optimized for round trips. And this was a list of results that was already filtered down with search criteria. They go hand in hand.
Take a look at 'logarithmic' pagination, as described in my answer here:
How to do page navigation for many, many pages? Logarithmic page navigation
It's like regular pagination, but solves the problem of getting to pages in the middle of a '...' range without many repeated mouseclicks. i.e. How long would it take to get to page 2456 out of 10380 if these are your links: 1 2 3 4 5 ... 10376 10377 10378 10379 10380 ?
(But, Pointy has, uhm... a point also (!))
Here is the code for a pure JavaScript pagination control I built recently. It is similar to your favorite with these added benefits...
Clicking the ... allows quick jump to any page
No words means no localization (next, prev, first, last buttons aren't
needed in this simple control)
No dependencies (jQuery not required)
var Pagination = {
code: '',
Extend: function(data) {
data = data || {};
Pagination.size = data.size || 300;
Pagination.page = data.page || 1;
Pagination.step = data.step || 3;
},
Add: function(s, f) {
for (var i = s; i < f; i++) {
Pagination.code += '<a>' + i + '</a>';
}
},
Last: function() {
Pagination.code += '<i>...</i><a>' + Pagination.size + '</a>';
},
First: function() {
Pagination.code += '<a>1</a><i>...</i>';
},
Click: function() {
Pagination.page = +this.innerHTML;
Pagination.Start();
},
Prev: function() {
Pagination.page--;
if (Pagination.page < 1) {
Pagination.page = 1;
}
Pagination.Start();
},
Next: function() {
Pagination.page++;
if (Pagination.page > Pagination.size) {
Pagination.page = Pagination.size;
}
Pagination.Start();
},
TypePage: function() {
Pagination.code = '<input onclick="this.setSelectionRange(0, this.value.length);this.focus();" onkeypress="if (event.keyCode == 13) { this.blur(); }" value="' + Pagination.page + '" /> / ' + Pagination.size;
Pagination.Finish();
var v = Pagination.e.getElementsByTagName('input')[0];
v.click();
v.addEventListener("blur", function(event) {
var p = parseInt(this.value);
if (!isNaN(parseFloat(p)) && isFinite(p)) {
if (p > Pagination.size) {
p = Pagination.size;
} else if (p < 1) {
p = 1;
}
} else {
p = Pagination.page;
}
Pagination.Init(document.getElementById('pagination'), {
size: Pagination.size,
page: p,
step: Pagination.step
});
}, false);
},
Bind: function() {
var a = Pagination.e.getElementsByTagName('a');
for (var i = 0; i < a.length; i++) {
if (+a[i].innerHTML === Pagination.page) a[i].className = 'current';
a[i].addEventListener('click', Pagination.Click, false);
}
var d = Pagination.e.getElementsByTagName('i');
for (i = 0; i < d.length; i++) {
d[i].addEventListener('click', Pagination.TypePage, false);
}
},
Finish: function() {
Pagination.e.innerHTML = Pagination.code;
Pagination.code = '';
Pagination.Bind();
},
Start: function() {
if (Pagination.size < Pagination.step * 2 + 6) {
Pagination.Add(1, Pagination.size + 1);
} else if (Pagination.page < Pagination.step * 2 + 1) {
Pagination.Add(1, Pagination.step * 2 + 4);
Pagination.Last();
} else if (Pagination.page > Pagination.size - Pagination.step * 2) {
Pagination.First();
Pagination.Add(Pagination.size - Pagination.step * 2 - 2, Pagination.size + 1);
} else {
Pagination.First();
Pagination.Add(Pagination.page - Pagination.step, Pagination.page + Pagination.step + 1);
Pagination.Last();
}
Pagination.Finish();
},
Buttons: function(e) {
var nav = e.getElementsByTagName('a');
nav[0].addEventListener('click', Pagination.Prev, false);
nav[1].addEventListener('click', Pagination.Next, false);
},
Create: function(e) {
var html = [
'<a>◄</a>', // previous button
'<span></span>', // pagination container
'<a>►</a>' // next button
];
e.innerHTML = html.join('');
Pagination.e = e.getElementsByTagName('span')[0];
Pagination.Buttons(e);
},
Init: function(e, data) {
Pagination.Extend(data);
Pagination.Create(e);
Pagination.Start();
}
};
var init = function() {
Pagination.Init(document.getElementById('pagination'), {
size: 30, // pages size
page: 1, // selected page
step: 2 // pages before and after current
});
};
document.addEventListener('DOMContentLoaded', init, false);
html {
height: 100%;
width: 100%;
background-color: #ffffff;
}
body {
margin: 0;
height: 100%;
width: 100%;
text-align: center;
font-family: Arial, sans-serif;
}
body:before {
content: '';
display: inline-block;
width: 0;
height: 100%;
vertical-align: middle;
}
#pagination {
display: inline-block;
vertical-align: middle;
padding: 1px 2px 4px 2px;
font-size: 12px;
color: #7D7D7D;
}
#pagination a,
#pagination i {
display: inline-block;
vertical-align: middle;
width: 22px;
color: #7D7D7D;
text-align: center;
padding: 4px 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
#pagination a {
margin: 0 2px 0 2px;
cursor: pointer;
}
#pagination a:hover {
background-color: #999;
color: #fff;
}
#pagination i {
border: 2px solid transparent;
cursor: pointer;
}
#pagination i:hover {
border: 2px solid #999;
cursor: pointer;
}
#pagination input {
width: 40px;
padding: 2px 4px;
color: #7D7D7D;
text-align: right;
}
#pagination a.current {
border: 1px solid #E9E9E9;
background-color: #666;
color: #fff;
}
<div id="pagination"></div>
There's a cool logarithmic pagination solution here:
http://jobcloud.cz/glPagiSmart.jc
But I'm not sure how many people would actually want to use the hex or binary implementations :)