I would like to include a flip counter on my site, similar to what Apple was using for their 1 billion app countdown.
Can anyone get their JavaScript to work standalone?
If anyone can provide working code, that would be great.
They're using a combination of CSS and JavaScript. The flip animation is powered by a CSS Sprite-like technique.
First of all, they have a very tall image called filmstrip.png that contains every flip "state" for each number (0 to 9; have a look at a scaled-down detail and you'll see what I mean).
Then, in the HTML, each digit is made up of three nested elements:
The first is a container element, which has its width and height set to the dimensions of a single flip "state", and its overflow set to hidden. This element is positioned relatively.
The second element is positioned absolutely (and because the first element is positioned relatively, this second element is positioned absolutely relative to the first element).
The third element has its background-image set to filmstrip.png, and its width and height set to the dimensions of this image.
The JavaScript then seems to rapidly change the top property of the second element, causing different parts of filmstrip.png to be exposed one after another, thus resulting in a flip animation.
Steve
Here it is, ready to be implemented in your own webpage
http://cnanney.com/journal/code/apple-style-counter-revisited/
I've made a counter that works great with very minimal javascript to give it a little "brain":
function Counter(selector, settings){
this.settings = Object.assign({
digits: 5,
delay: 250, // ms
direction: '' // ltr is default
}, settings||{})
var scopeElm = document.querySelector(selector)
// generate digits markup
var digitsHTML = Array(this.settings.digits + 1).join('<div><b data-value="0"></b></div>')
scopeElm.innerHTML = digitsHTML;
this.DOM = {
scope : scopeElm,
digits : scopeElm.querySelectorAll('b')
}
this.DOM.scope.addEventListener('transitionend', e => {
if (e.pseudoElement === "::before" && e.propertyName == 'margin-top'){
e.target.classList.remove('blur')
}
})
this.count()
}
Counter.prototype.count = function(newVal){
var countTo, className,
settings = this.settings,
digitsElms = this.DOM.digits;
// update instance's value
this.value = newVal || this.DOM.scope.dataset.value|0
if( !this.value ) return;
// convert value into an array of numbers
countTo = (this.value+'').split('')
if(settings.direction == 'rtl'){
countTo = countTo.reverse()
digitsElms = [].slice.call(digitsElms).reverse()
}
// loop on each number element and change it
digitsElms.forEach(function(item, i){
if( +item.dataset.value != countTo[i] && countTo[i] >= 0 )
setTimeout(function(j){
var diff = Math.abs(countTo[j] - +item.dataset.value);
item.dataset.value = countTo[j]
if( diff > 3 )
item.className = 'blur';
}, i * settings.delay, i)
})
}
/////////////// create new counter for this demo ///////////////////////
var counter = new Counter('.numCounter', {direction:'rtl', delay:200, digits:7})
setInterval(randomCount, 3000)
function randomCount(){
counter.count( getRandomNum(0, 9999999))
}
function getRandomNum(min,max){
return Math.floor(Math.random()*(max-min+1) + min)
}
.numCounter {
display: inline-block;
height: 90px;
line-height: 90px;
text-shadow: 0 0 2px #fff;
font-weight: bold;
white-space: normal;
font-size: 50px;
}
.numCounter > div {
display: inline-block;
vertical-align: top;
height: 100%;
}
.numCounter > div > b {
display: inline-block;
width: 40px;
height: 100%;
margin: 0 0.1em;
border-radius: 8px;
text-align: center;
background: white;
overflow: hidden;
}
.numCounter > div > b::before {
content: ' 0 1 2 3 4 5 6 7 8 9 ';
display: block;
word-break: break-all;
-webkit-transition: 0.5s cubic-bezier(0.75, 0.15, 0.6, 1.15), text-shadow 150ms;
transition: 0.5s cubic-bezier(0.75, 0.15, 0.6, 1.15), text-shadow 150ms;
}
.numCounter > div > b.blur {
text-shadow: 2px 1px 3px rgba(0, 0, 0, 0.2),
0 0.1em 2px rgba(255, 255, 255, 0.6),
0 0.3em 3px rgba(255, 255, 255, 0.3),
0 -0.1em 2px rgba(255, 255, 255, 0.6),
0 -0.3em 3px rgba(255, 255, 255, 0.3);
}
.numCounter > div > b[data-value="1"]::before { margin-top: -90px; }
.numCounter > div > b[data-value="2"]::before { margin-top: -180px;}
.numCounter > div > b[data-value="3"]::before { margin-top: -270px;}
.numCounter > div > b[data-value="4"]::before { margin-top: -360px;}
.numCounter > div > b[data-value="5"]::before { margin-top: -450px;}
.numCounter > div > b[data-value="6"]::before { margin-top: -540px;}
.numCounter > div > b[data-value="7"]::before { margin-top: -630px;}
.numCounter > div > b[data-value="8"]::before { margin-top: -720px;}
.numCounter > div > b[data-value="9"]::before { margin-top: -810px;}
.numCounter > div:nth-last-child(3n)::before {
content: ",";
display: inline;
font-size: 1.1em;
opacity: .6;
color: white;
}
html, body {
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
font-family: Arial;
}
.numCounter {
overflow: hidden;
padding: .4em;
text-align: center;
border-radius: 16px;
background: black;
}
.numCounter b {
color: black;
}
<div class='numCounter' data-value='1839471'></div>
It looks great and performs live very well, and it it count from any number to any number.
While searching for the same thing I found a commercial product offering this functionality: Sprite Countdown Flip.
Note: I'm not affiliated with this product; but it's well done and might be useful to someone.
I recommend the open source variant: FlipclockJS, which probably was created right after this event :)
Github: objectivehtml/FlipClock, available via NPM and Bower (not maintained)
Related
I have a small page. Divas in the form of circles are created here every certain time.
They spawn in random places.
As can be seen even on the buttons and slightly outside the page.
The question is. Is it possible to make a box that does not touch the buttons, and that the circles are created within this box?
This should be done as a border with a certain extension, but specifying everything in pixels is not an option, it will be bad for different screens.
I created such a frame, replaced document.body.appendChild(div);
on the document.getElementById("spawnRadius").appendChild(div);
It seems that they should appear within this frame, but no, all the same throughout the page.
I also tried instead of whole page height and width document.documentElement.clientWidth use the width and height of the desired border spawnRadius.width
But now all my circles do not appear randomly, but at the beginning of this block in one place.
I tried to see these values through console.log
console.log(documentHeight);
console.log(documentWidth);
But getting there undefined
PS. Demo watch in full page
//timer
var minutesLabel = document.getElementById("minutes");
var secondsLabel = document.getElementById("seconds");
var totalSeconds = 0;
setInterval(setTime, 1000);
function setTime() {
++totalSeconds;
secondsLabel.innerHTML = pad(totalSeconds % 60);
minutesLabel.innerHTML = pad(parseInt(totalSeconds / 60));
}
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
//create circle
var widthHeight = 65;
var margin = 25;
var delta = widthHeight + margin;
var spawnRadius = document.getElementById("spawnRadius");
let clicks = 0;
function createDiv(id, color) {
let div = document.createElement('div');
var currentTop = 0;
var documentHeight = spawnRadius.height;
var documentWidth = spawnRadius.width;
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.borderColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.borderColor = color;
}
div.classList.add("circle");
div.classList.add("animation");
currentTop = Math.floor(Math.random() * documentHeight) - delta;
currentLeft = Math.floor(Math.random() * documentWidth) - delta;
var limitedTop = Math.max(margin * -1, currentTop);
var limitedLeft = Math.max(margin * -1, currentLeft);
div.style.top = limitedTop + "px";
div.style.left = limitedLeft + "px";
const nodes = document.querySelectorAll('.animation');
for(let i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', (event) => {
event.target.style.animation = 'Animation 200ms linear';
setTimeout(() => {
event.target.style.animation = '';
}, 220); });
}
$(div).click(function() {
$('#clicks').text(++clicks);
$(this).fadeOut();
});
document.getElementById("spawnRadius").appendChild(div);
}
let i = 0;
const oneSecond = 600;
setInterval(() => {
i += 1;
createDiv(`circle${i}`);
}, oneSecond);
html, body {
width: 100%;
height: 100%;
margin: 0;
background: #0f0f0f;
}
.back {
font-family: "Comic Sans MS", cursive, sans-serif;
font-size: 25px;
letter-spacing: 2px;
word-spacing: 2px;
color: #ffffff;
text-shadow: 0 0 5px #ffffff, 0 0 10px #ffffff, 0 0 20px #ffffff, 0 0 40px #ff00de, 0 0 80px #ff00de, 0 0 90px #ff00de, 0 0 100px #ff00de, 0 0 150px #ff00de;
font-weight: 700;
text-decoration: none;
font-style: italic;
font-variant: normal;
text-transform: lowercase;
position: absolute;
top: 25%;
left: 2%;
user-select: none;
z-index: 999;
}
.panel {
color: #0f0f0f;
font-size: 40px;
z-index: 999;
position: absolute;
cursor: default;
user-select: none;
color: #0f0f0f;
}
.score {
border: 1px solid #ffffff;
padding: 5px;
background-color: #ffffff;
border-radius: 40px 10px;
}
.time {
border: 1px solid #ffffff;
padding: 5px;
background-color: #ffffff;
border-radius: 40px 10px;
}
.circle {
width: 60px;
height: 60px;
border-radius: 60px;
background-color: #0f0f0f;
border: 3px solid #000;
margin: 20px;
position: absolute;
}
#keyframes Animation {
0% {
transform: scale(1);
}
50% {
transform: scale(.8);
}
100% {
transform: scale(1);
}
}
#spawnRadius {
top: 55%;
height: 650px;
width: 1000px;
left: 50%;
white-space: nowrap;
position: absolute;
transform: translate(-50%, -50%);
background: #0f0f0f;
border: 2px solid #ebc6df;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span class="panel">
<span class="score">Score: <a id="clicks">0</a></span>
<span class="time">Time: <label id="minutes">00</label>:<label id="seconds">00</label></span>
</span>
back
<div id="spawnRadius"></div>
To answer your main question, the getBoundingClientRect method can be used to retrieve the current bounding rectangle of an element, using which you can determine where the valid spawn areas are.
When choosing a valid placement, only consider the width and height of the container element, since the coordinates of child elements are relative to its parent. You also need to take into account the size of the elements being spawned, so the valid range of the x position for example is 0 to containerWidth - circleWidth.
The circles also had a CSS margin associated with them, which would offset them past their absolute coordinates.
There are a few other issues with the code though which you may run into later on:
There was an odd mix of jQuery and standard JavaScript calls, so if you're familiar with native JavaScript methods then it's likely simpler to stick with those and remove the dependency on jQuery.
For example, there were two click event handlers on each circle, one to add the CSS animation and another to increment the score. These can be combined into a single function.
The bounce out animation and the jQuery fade out can also be combined by adding opacity values into the animation start and end keyframes.
There was a loop in the createDiv function which added another click event handler to every circle element rather than just to the newly created element. This may have originally necessitated the jQuery click handler outside of that loop, since otherwise the score counter would have been incremented multiple times.
It was also possible to click the circles multiple times before the animation was complete (hence adding multiple points), which was likely not intended. Adding a simple Boolean clicked flag can avoid this.
Once the fade animation completed, the circle element itself was still on the page, it just had a display of none so wouldn't be visible. Over time, this would cause slowdowns on lower end hardware since there would be many DOM elements still sitting in memory that were no longer required. As such, it's best to remove elements from the DOM once they're no longer needed using removeChild. You had the right idea by removing the animation after the animation completed.
Here's the amended code:
var minutesLabel = document.getElementById("minutes");
var secondsLabel = document.getElementById("seconds");
var clickEl = document.getElementById("clicks");
var totalSeconds = 0;
let clicks = 0;
setInterval(setTime, 1000);
function setTime() {
++totalSeconds;
secondsLabel.innerText = pad(totalSeconds % 60);
minutesLabel.innerText = pad(parseInt(totalSeconds / 60));
}
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
var spawnRadius = document.getElementById("spawnRadius");
var spawnArea = spawnRadius.getBoundingClientRect();
const circleSize = 66; // Including borders
//create circle
function createDiv(id, color) {
let div = document.createElement('div');
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.borderColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.borderColor = color;
}
// Randomly position circle within spawn area
div.style.top = `${Math.floor(Math.random() * (spawnArea.height - circleSize))}px`;
div.style.left = `${Math.floor(Math.random() * (spawnArea.width - circleSize))}px`;
div.classList.add("circle", "animation");
// Add click handler
let clicked = false;
div.addEventListener('click', (event) => {
if (clicked) { return; } // Only allow one click per circle
clicked = true;
div.style.animation = 'Animation 200ms linear forwards';
setTimeout(() => { spawnRadius.removeChild(div); }, 220);
clickEl.innerText = ++clicks
});
spawnRadius.appendChild(div);
}
let i = 0;
const oneSecond = 1000;
setInterval(() => {
i += 1;
createDiv(`circle${i}`);
}, oneSecond);
html, body {
width: 100%;
height: 100%;
margin: 0;
background: #0f0f0f;
}
.back {
font-family: "Comic Sans MS", cursive, sans-serif;
font-size: 25px;
letter-spacing: 2px;
word-spacing: 2px;
color: #ffffff;
text-shadow: 0 0 5px #ffffff, 0 0 10px #ffffff, 0 0 20px #ffffff, 0 0 40px #ff00de, 0 0 80px #ff00de, 0 0 90px #ff00de, 0 0 100px #ff00de, 0 0 150px #ff00de;
font-weight: 700;
text-decoration: none;
font-style: italic;
font-variant: normal;
text-transform: lowercase;
position: absolute;
top: 25%;
left: 2%;
user-select: none;
z-index: 999;
}
.panel {
color: #0f0f0f;
font-size: 40px;
z-index: 999;
position: absolute;
cursor: default;
user-select: none;
color: #0f0f0f;
}
.score {
border: 1px solid #ffffff;
padding: 5px;
background-color: #ffffff;
border-radius: 40px 10px;
}
.time {
border: 1px solid #ffffff;
padding: 5px;
background-color: #ffffff;
border-radius: 40px 10px;
}
.circle {
width: 60px;
height: 60px;
border-radius: 60px;
background-color: #0f0f0f;
border: 3px solid #000;
position: absolute;
}
#keyframes Animation {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(.8);
}
100% {
transform: scale(1);
opacity: 0;
}
}
#spawnRadius {
top: 55%;
height: 650px;
width: 1000px;
left: 50%;
white-space: nowrap;
position: absolute;
transform: translate(-50%, -50%);
background: #0f0f0f;
border: 2px solid #ebc6df;
}
<span class="panel">
<span class="score">Score: <a id="clicks">0</a></span>
<span class="time">Time: <label id="minutes">00</label>:<label id="seconds">00</label></span>
</span>
back
<div id="spawnRadius"></div>
In my application, there are multiple cards and left/right paddles. For normal browsing(safari,firefox,chrome) in desktop/laptop, that left/right paddles are visible while hovering over those cards. Inside those cards all the link/hyperlink are working fine in desktop/laptop
I am using #mouseover to show those paddles to scroll those cards. In ipad(touch devices), that problem occurs. Because hover effect is not working in touch devices.
Problem
Those paddles are visible only after first click because hovering is not working for touch devices.
So, i want to disable the hover for touch devices and show the paddles on first rendering. I have checked media queries but it is not working. I am not using jquery. So, how can i show those paddles on initial loading for touch device using vue js.
Code snippet
<div
v-show="grid"
class="second-row"
#mouseover="showPaddle"
#scroll.passive="setScrolledLeft"
>
<div class="paddles">
<button
v-if="showRightPaddle"
tabindex="-1"
class="right-paddle paddle"
#click="scrollCards('right')"
>
<i class="el-icon-arrow-right-fill" />
</button>
<button
v-if="showLeftPaddle"
tabindex="-1"
class="left-paddle paddle"
#click="scrollCards('left')"
>
<i class="el-icon-arrow-left-fill" />
</button>
</div>
....card details html
</div>
Method Code snippet to show Paddles
showPaddle() {
const secondRowEl = this.$el.querySelector('.second-row');
const gridWidth = this.$el.querySelector('.grid').clientWidth;
const scrollMax = gridWidth - secondRowEl.clientWidth;
if (gridWidth > secondRowEl.clientWidth) {
if (secondRowEl.scrollLeft > 0 && secondRowEl.scrollLeft < scrollMax) {
this.showLeftPaddle = true;
this.showRightPaddle = true;
} else if (secondRowEl.scrollLeft >= scrollMax) {
this.showLeftPaddle = true;
this.showRightPaddle = false;
} else if (secondRowEl.scrollLeft <= 0) {
this.showLeftPaddle = false;
this.showRightPaddle = true;
}
} else {
this.showLeftPaddle = false;
this.showRightPaddle = false;
}
}
Css style
.second-row {
margin: 0 auto;
padding: 0 0 0 4px;
overflow-y: hidden;
background: var(--gray40);
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
&:hover,
&:focus {
.paddles {
display: block;
}
}
.paddles {
display: none;
.paddle {
border: none;
padding: 0;
height: 100px;
width: 50px;
cursor: pointer;
position: fixed;
margin-top: 33vh;
z-index: 10;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
text-align: center;
font-size: 2.5em;
color: #333333;
background-color: rgba(204, 204, 204, 0.5);
&:hover {
background-color: rgba(204, 204, 204, 0.65);
}
&:focus {
background-color: rgba(204, 204, 204, 0.8);
}
border-radius: 50px;
}
.left-paddle {
&.paddle {
> i {
margin-left: -10px;
font-size: 44px;
position: relative;
top: 2px;
}
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
.right-paddle {
right: 0;
&.paddle {
> i {
margin-right: -10px;
font-size: 44px;
position: relative;
top: 2px;
}
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
}
.hidden {
display: none;
}
.grid {
display: block;
&.product-grid {
position: relative;
height: calc(100vh - 194px);
margin-bottom: 0;
.item {
position: absolute;
width: rem(442);
padding-right: rem(8);
margin: 0;
&.highlight {
border: 1px solid #66afe9;
border-radius: rem(4);
outline: 0;
box-shadow: 0 1px 1px 4px rgba(102, 175, 233, 0.6),
0 0 2px rgba(102, 175, 233, 0.6);
-moz-box-shadow: 0 1px 1px 4px rgba(102, 175, 233, 0.6),
0 0 2px rgba(102, 175, 233, 0.6);
-webkit-box-shadow: 0 1px 1px 4px rgba(102, 175, 233, 0.6),
0 0 2px rgba(102, 175, 233, 0.6);
}
.app-card {
.app-card__header {
padding: 0.25rem 0.75rem;
i {
position: relative;
top: rem(3);
}
.app-card__title {
&:first-child {
margin-left: 0;
}
}
.app-card__expand-icon {
margin-right: 0;
margin-top: 0.5rem;
height: 1rem;
right: 0.75rem;
i {
position: static;
color: var(--gray70);
}
}
}
}
}
}
}
}
Can you help on this to disable hover effect for touch devices and show paddles on initial rendering?
Unfortunately no perfect solution to this problem yet exists, and is unlikely to exists in the future. “The Web is meant to be accessible to everyone, regardless of which browser or device they're using.” This implies all content should be visible, and serving different content based on device type is frowned upon by the W3C WAI
However there is a difference between changing the way content appears vs. completely hiding content from users if they’re viewing on different devices. Your scenario falls into the first category, which in my opinion is fine.
Touchscreen devices - smartphone or tablet - cannot support hover events. Rendering the page differently depending on which user agent (device) your site is viewed in is possible, and you have a couple of options. However in general, using the user agent to detect the browser looks simple, but doing it well is, in fact, a very hard problem. Of the two options detailed below, if I had to, I’d go for option 2.
Detect the user agent using javascript.
NOT recommended— read more about the issues here.
let hasTouchScreen = false;
if ("maxTouchPoints" in navigator) {
hasTouchScreen = navigator.maxTouchPoints > 0;
} else if ("msMaxTouchPoints" in navigator) {
hasTouchScreen = navigator.msMaxTouchPoints > 0;
} else {
let mQ = window.matchMedia && matchMedia("(pointer:coarse)");
if (mQ && mQ.media === "(pointer:coarse)") {
hasTouchScreen = !!mQ.matches;
} else if ('orientation' in window) {
hasTouchScreen = true; // deprecated, but good fallback
} else {
// Only as a last resort, fall back to user agent sniffing
var UA = navigator.userAgent;
hasTouchScreen = (
/\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) ||
/\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA)
);
}
}
if (hasTouchScreen) {
// do some stuff on devices with smaller screens
}
Detect screen-size with javascript (CSS media queries are also an option, but you mention you’ve tried them already).
NOT recommended. The iPad Pro 12 has a resolution comparable to a desktop devices, for example.
if (window.innerWidth < 768) {
// do some stuff on devices with screens smaller than
// 768px wide
window.addEventListener("resize", function() {
/*refresh screen size dependent things*/
})
}
I'm experiencing a JavaScript error on a pen on codepen.io.
TypeError: window.CP is undefined
I tried to look it up and understood that it's connected to an infinite loop protection, but I can't find a way to solve the problem.
Here is the link to the pen on CodePen (where it doesn't work) and to a JSFiddle (where it works).
Here is the code on the snippet (where it also works).
(The green block is supposed to change color when you scroll)
<script>
function scrollFunction() {
var content = document.getElementById('content').querySelectorAll('p')
var contentY = []
for (i = 0; i < content.length; i++) {
contentY[i] = content[i].offsetTop
}
var html = document.documentElement
var y = html.scrollTop
var windowY = window.innerHeight
var phone = document.getElementById('phone')
for (i = 0; i < content.length; i++) {
if (y > contentY[i] - windowY * 0.4) {
phone.classList.add('color' + (i + 1))
phone.classList.remove('color' + i)
} else {
phone.classList.remove('color' + (i + 1))
}
}
}
window.onscroll = function () {
scrollFunction()
}
</script>
body {
background: white;
color: #323232;
font-weight: 300;
height: 100vh;
margin: 0;
font-family: Helvetica neue, roboto;
}
nav {
position: fixed;
top: 0;
left: 0;
width: 100%;
background: white;
-webkit-box-shadow: 0px -6px 25px 20px rgba(0, 0, 0, 0.44);
-moz-box-shadow: 0px -6px 25px 20px rgba(0, 0, 0, 0.44);
box-shadow: 0px -6px 25px 20px rgba(0, 0, 0, 0.44);
}
nav ul {
list-style: none;
display: flex;
flex-wrap: wrap;
}
nav ul li {
padding: 0 1rem;
}
main {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}
#content {
width: 50%;
}
/* The first paragraph has a margin-top = the size of the screen*/
#content p:first-child {
margin-top: 100vh;
}
#content p {
margin: 0;
margin-bottom: 100vh;
}
/* Same margin-top as the 1st paragraph + sticky at 40% from the top*/
#phone {
margin-top: 100vh;
width: 8rem;
height: 13rem;
max-height: 70vh;
position: sticky;
top: 40%;
background: lightgreen;
transition: background 0.2s;
}
#phone.color1 {
background: palevioletred;
}
#phone.color2 {
background: purple;
}
#phone.color3 {
background: royalblue;
}
#phone.color4 {
background: rgb(30, 150, 104);
}
<nav class="menu">
<ul>
<li>Menu</li>
<li>Bar</li>
<li>Scrolling</li>
<li>Effect</li>
</ul>
</nav>
<main>
<div id="content" class="content">
<p>
One advanced diverted domestic sex repeated bringing you old. Possible procured her trifling laughter thoughts property she met way.
</p>
<p>
Finished her are its honoured drawings nor. Pretty see mutual thrown all not edward ten. Particular an boisterous up he reasonably frequently.
</p>
<p>
May musical arrival beloved luckily adapted him. Shyness mention married son she his started now. Rose if as past near were. To graceful he elegance oh moderate attended entrance pleasur
</p>
<p>
Out believe has request not how comfort evident. Up delight cousins we feeling minutes.
</p>
</div>
<div id="phone">
</div>
</main
CodePen has a problem with the normal loops but working well with Array methods like: forEach, map, or reduce.
Chrome console output:
Uncaught TypeError: Cannot read property ‘shouldStopExecution’ of undefined
In order to solve it, I had to change the normal loop with forEach instead. Example
I'm trying to create some very basic tooltips but I'm having trouble calculating the exact position these should go in with some JavaScript. The reason for wanting a fixed position is to make sure these work whenever there is overflow hidden and such.
This is my code so far:
var overflowTooltip = function (elem) {
let legendRow = elem.currentTarget.getBoundingClientRect();
let tooltip = elem.currentTarget.children[2];
let topPosition;
let leftPosition;
if ((elem.currentTarget.offsetWidth < elem.currentTarget.scrollWidth) && tooltip !== undefined) {
tooltip.classList.add('total-opacity');
$timeout(function () {
if (tooltip.offsetHeight > 35) {
topPosition = (legendRow.top - tooltip.offsetHeight / 4) - 65;
} else {
topPosition = legendRow.top - 65;
}
leftPosition = (legendRow.left + elem.currentTarget.offsetWidth) / 2;
tooltip.style.left = leftPosition + 'px';
tooltip.style.top = topPosition + 'px';
$timeout(function () {
tooltip.classList.remove('total-opacity');
}, 400)
}, 100);
} else {
elem.currentTarget.children[2].style.left = '-9999px';
}
}
And some SASS:
.custom-tooltip {
font-family: $brand-font-condensed;
font-size: 14px;
font-weight: 400;
position: fixed;
text-align: left;
overflow: visible !important;
background-color: rgba($dark-gray, 0.95);
color: #fff;
height: auto;
padding: 7px 10px;
z-index: 9000;
visibility: hidden;
opacity: 0;
border-radius: 2px;
box-shadow: 0 2px 5px 0 rgba(#000, 0.16), 0 2px 10px 0 rgba(#000, 0.12);
#include transition (.2s ease-in);
left: -9999px;
&:hover {
visibility: visible;
opacity: 1;
#include transition (.2s ease-out);
-webkit-transition-delay: .3s;
transition-delay: .3s;
}
}
The above works but it's not perfect. if I wanna change around the position for it to appear on the left, right, bottom. I'd have to so some refactoring. If the tooltip's height is bigger or smaller, the position changes, If I scroll up or down, the tooltip stays stuck on screen for a few seconds. etc. Lots of these little details which are pretty annoying.
Not interested in using a plugin at the moment nor jQuery. Thanks for any suggestion or feedback :)
how do I select elements created dinamicamentes by groups? I want to select msgpvtstyleme and work on them. Then select msgpvtstyle again and work on them ... Goal is to get grouped elements and insert them classes .....
I want to simulate chat balloons
result final! Thank you all!
Here is a basic jQuery script which checks each element and detecting the following:
Check current class
If previous sibling has a different class then it will get first
If next sibling is of the same class, it will be middle
If next sibling is of another class, it will be marked as last
If previous and next siblings are of other classes, it will be first middle last
// define your container here
var container = $('.container'),
currentClass = container.children(":first").attr("class");
// run through each child
container.children('li').each(function() {
currentClass = $(this).attr("class");
if ( $(this).prev().attr("class") !== currentClass ) {
$(this).attr("data-order","first"); }
if ( $(this).next().attr("class") === currentClass && $(this).prev().attr("class") === currentClass ) {
$(this).attr("data-order","middle"); }
if ( $(this).next().attr("class") !== currentClass ) {
$(this).attr("data-order","last"); }
if ( $(this).next().attr("class") !== currentClass && $(this).prev().attr("class") !== currentClass ) {
$(this).attr("data-order","first middle last"); }
// debugging only
$(this).text( $(this).attr("class") + ': ' + $(this).attr('data-order') );
});
li[data-order~="first"] {font-weight: bold;}
li[data-order~="last"] {border-bottom:1px solid;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="container">
<li class="class1"></li>
<li class="class1"></li>
<li class="class1"></li>
<li class="class2"></li>
<li class="class2"></li>
<li class="class1"></li>
</ul>
jsFiddle: https://jsfiddle.net/azizn/40trqhn4/
Note: I used data-order attribute instead class as altering the class name will break the checking function (since it all revolves around class name). You can access the elements through the CSS attribute selectors [data-order="first"] for example.
Here is a simple way:
function style_latest_messages() {
var classes = '.msgpvtstyle, .msgpvtstyleme';
$(classes).filter('.first,.middle,.last')
.last().removeClass('first middle last')
.add($(classes).not('.first,.middle,.last')).each(function() {
styleMessage(this);
});
function styleMessage(el) {
var prev = $(el).prevAll(classes).eq(0),
next = $(el).nextAll(classes).eq(0),
is_first = is_me(prev) != is_me(el) || !prev.length,
is_last = is_me(next) != is_me(el) || !next.length;
if(is_first) $(el).addClass('first');
if(is_last) $(el).addClass('last');
if(is_first == is_last) $(el).addClass('middle');
}
}
function is_me(el) { return $(el).hasClass('msgpvtstyleme'); }
Note that $(this).prevAll(classes).eq(0) and $(this).nextAll(classes).eq(0) will allow you to ignore any extra element, such as time. Try the demo to see what I mean.
Demo
style_latest_messages();
function style_latest_messages() {
var classes = '.msgpvtstyle, .msgpvtstyleme';
$(classes).filter('.first,.middle,.last')
.last().removeClass('first middle last')
.add($(classes).not('.first,.middle,.last')).each(function() {
styleMessage(this);
});
function styleMessage(el) {
var prev = $(el).prevAll(classes).eq(0),
next = $(el).nextAll(classes).eq(0),
is_first = is_me(prev) != is_me(el) || !prev.length,
is_last = is_me(next) != is_me(el) || !next.length;
if(is_first) $(el).addClass('first');
if(is_last) $(el).addClass('last');
if(is_first == is_last) $(el).addClass('middle');
}
}
function is_me(el) { return $(el).hasClass('msgpvtstyleme'); }
// Just for testing
var msgs=['<li class="time">17:52</li><li class="msgpvtstyleme">Don\'t forget to unload the dishwasher</li>', '<li class="msgpvtstyleme">Did you finish your homework?</li>', '<li class="msgpvtstyleme">Your grandmother is coming for dinner.</li>', '<li class="msgpvtstyleme">Dad and I decided to buy you a car.</li>', '<li class="time">17:56</li><li class="msgpvtstyle">Did U? OMG thank U!</li>', '<li class="msgpvtstyleme">No I was just making sure you get my texts.</li>', '<li class="msgpvtstyle">That was cruel</li>']; (function add_msg(){if(!msgs.length)return;$('.messagepvt').append(msgs.shift());style_latest_messages();setTimeout(add_msg,(msgs[0] || "").length * 50)})();
.messagepvt{ position: absolute; top: 0; left: 50%; height: 100%; overflow: hidden; margin-left: -35%; list-style: none; font-family: Arial, Helvetica, sans-serif; width: 70%; margin-top: 0; background: #f5f5f5; padding: .5em }body{ overflow: hidden }.messagepvt:after{ content: ""; display: block; clear: both }.time{ text-align: center; clear: both; color: #888; font-size: 10px; margin: .5em }.msgpvtstyle, .msgpvtstyleme { padding: .3em .8em; float: left; clear: both; background: #e6e6ec; color: #000; margin: 1px; font-size: 12px; -webkit-transition: border-radius .25s ease; -moz-transition: border-radius .25s ease; -webkit-animation: deploy .15s ease; -moz-animation: deploy .15s ease; -webkit-transform-origin: top left; -moz-transform-origin: top left }.msgpvtstyle.first { border-radius: 1.5em 1.5em 1.5em .5em }.msgpvtstyle.middle { border-radius: .5em 1.5em 1.5em .5em }.msgpvtstyle.last { border-radius: .5em 1.5em 1.5em 1.5em }.msgpvtstyle.first.middle.last, .msgpvtstyleme.first.middle.last { border-radius: 1.5em 1.5em 1.5em 1.5em }.msgpvtstyleme { float: right; background: #49f; color: #fff; -webkit-transform-origin: top right; -moz-transform-origin: top right}.msgpvtstyleme.first { border-radius: 1.5em 1.5em .5em 1.5em }.msgpvtstyleme.middle { border-radius: 1.5em .5em .5em 1.5em }.msgpvtstyleme.last { border-radius: 1.5em .5em 1.5em 1.5em }#-webkit-keyframes deploy{ from{-webkit-transform: scale(0)}to{-webkit-transform: scale(1)} }#-moz-keyframes deploy{ from{-moz-transform: scale(0)}to{-moz-transform: scale(1)} }
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="messagepvt"></ul>
Try this code to add classes in existing html tags
$('.msgpvtstyle ').each(function() {
$('.msgpvtstyleme:first-child' , $(this)).addClass('first');
$('.msgpvtstyleme:last-child' , $(this)).addClass('last');
$('.msgpvtstyleme:not(:first-child):not(:last-child)' , $(this)).addClass('middle');
});