Visible intersectionObserver - javascript

I am trying to learn about Javascript's IntersectionObserver.
After reading several articles and the documentation I have decided to make a CodePen to try it myself: IntersectionObserver CodePen
I would like to display the "block that is visible" on the top message. The CodePen "almost" works, but not completely. Sometimes it shows the correct block, sometimes it doesn't.
Here is my JS:
let message = document.querySelector('#block-number');
// INTERSECTION OBSERVER STUFF
const io = new IntersectionObserver(entries => {
if(entries[0].isIntersecting) {
message.innerHTML = entries[0].target.textContent;
}
}, {
threshold: [.25]
});
// ELEMENTS TO OBSERVE
const blk1 = document.querySelector('#block1');
const blk2 = document.querySelector('#block2');
const blk3 = document.querySelector('#block3');
const blk4 = document.querySelector('#block4');
const blk5 = document.querySelector('#block5');
const blk6 = document.querySelector('#block6');
// START OBSERVING ELEMENTS
io.observe(blk1);
io.observe(blk2);
io.observe(blk3);
io.observe(blk4);
io.observe(blk5);
io.observe(blk6);
Any ideas on what i am doing wrong?
i have also tried (without luck) something like:
if(entries[0].intersectionRatio !== 0)
Thank you!

The function passed to the IntersectionObserved is executed when the intersection state changes. So what happens when you are at block 3 and scroll a bit so block 4 is shown? The intersection changes for block 4 and so the message is changed. WHen you scroll back up the intersection is changed again for block 4, but it does not enter the if condition. The intersection for block 3 on the other hand is not changed - it was visible before, even though not fully, it's visible still.
There are few ways you can fix this.
One is to define intersection ratio, and going above and below that ratio will be considered change in the state (pass options hash as second argument, containing threshold key with value 0 - 1, e.g. 0.5 for 50% visibility)
You can also add the same observer for all of the blocks and iterate trough entries in the function, checking which block has the best intersection ratio.

You have set the threshold to 25%.
The problem with this, is that the previous block leaves its last 25% of the viewport after the next block enters the viewport by 25%.
This was easy to see with the following console.log:
console.log(entries[0].target.textContent, ": ", entries[0].intersectionRatio)
let message = document.querySelector('#block-number');
// INTERSECTION OBSERVER STUFF
const io = new IntersectionObserver(entries => {
if(entries[0].isIntersecting ) {
console.log(entries[0].target.textContent, ": ", entries[0].intersectionRatio)
message.innerHTML = entries[0].target.textContent;
}
}, {
threshold: [.25]
});
// ELEMENTS TO OBSERVE
const blk1 = document.querySelector('#block1');
const blk2 = document.querySelector('#block2');
const blk3 = document.querySelector('#block3');
const blk4 = document.querySelector('#block4');
const blk5 = document.querySelector('#block5');
const blk6 = document.querySelector('#block6');
// START OBSERVING ELEMENTS
io.observe(blk1);
io.observe(blk2);
io.observe(blk3);
io.observe(blk4);
io.observe(blk5);
io.observe(blk6);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: roboto;
}
.center {
display: flex;
align-items: center;
justify-content: center;
}
.container {
background-color: #eee;
width: 100%;
height: 100%;
min-height: 100vh;
}
.message {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 80px;
background-color: #ef9b8d;
color: white;
}
.blocks {
padding-top: 100px;
}
.block {
height: 85vh;
width: 90vw;
margin: 0 auto 15vh;
background-color: #999;
color: white;
}
<div class="message center">Displaying <span id="block-number">Block 1</span></div>
<div class="blocks">
<div id="block1" class="block center">Block 1</div>
<div id="block2" class="block center">Block 2</div>
<div id="block3" class="block center">Block 3</div>
<div id="block4" class="block center">Block 4</div>
<div id="block5" class="block center">Block 5</div>
<div id="block6" class="block center">Block 6</div>
</div>
To fix this, simply raise the threshold. (Depending on how much of the block needs to enter the viewport for you to consider it the current block)
Demo:
let message = document.querySelector('#block-number');
// INTERSECTION OBSERVER STUFF
const io = new IntersectionObserver(entries => {
if(entries[0].isIntersecting ) {
console.log(entries[0].target.textContent, ": ", entries[0].intersectionRatio)
message.innerHTML = entries[0].target.textContent;
}
}, {
threshold: [.8] // raised the threshold
});
// ELEMENTS TO OBSERVE
const blk1 = document.querySelector('#block1');
const blk2 = document.querySelector('#block2');
const blk3 = document.querySelector('#block3');
const blk4 = document.querySelector('#block4');
const blk5 = document.querySelector('#block5');
const blk6 = document.querySelector('#block6');
// START OBSERVING ELEMENTS
io.observe(blk1);
io.observe(blk2);
io.observe(blk3);
io.observe(blk4);
io.observe(blk5);
io.observe(blk6);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: roboto;
}
.center {
display: flex;
align-items: center;
justify-content: center;
}
.container {
background-color: #eee;
width: 100%;
height: 100%;
min-height: 100vh;
}
.message {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 80px;
background-color: #ef9b8d;
color: white;
}
.blocks {
padding-top: 100px;
}
.block {
height: 85vh;
width: 90vw;
margin: 0 auto 15vh;
background-color: #999;
color: white;
}
<div class="message center">Displaying <span id="block-number">Block 1</span></div>
<div class="blocks">
<div id="block1" class="block center">Block 1</div>
<div id="block2" class="block center">Block 2</div>
<div id="block3" class="block center">Block 3</div>
<div id="block4" class="block center">Block 4</div>
<div id="block5" class="block center">Block 5</div>
<div id="block6" class="block center">Block 6</div>
</div>

Related

Counter Observe when I scroll

Problem
I created a counter using HTML, CSS and JS (such as satisfied customer numbers, branch numbers, etc.)
The counter is also animated but since it's down the page, I'd like to animate it only when it gets to that point on the page. How do I do with the js?
const counters = document.querySelectorAll('.value');
const speed = 400;
counters.forEach( counter => {
const animate = () => {
const value = +counter.getAttribute('akhi');
const data = +counter.innerText;
const time = value / speed;
if(data < value) {
counter.innerText = Math.ceil(data + time);
setTimeout(animate, 1);
}else{
counter.innerText = value;
}
}
animate();
});
.counter-box {
display: block;
background: #f6f6f6;
padding: 40px 20px 37px;
text-align: center
}
.counter-box p {
margin: 5px 0 0;
padding: 0;
color: #909090;
font-size: 18px;
font-weight: 500
}
.counter {
display: block;
font-size: 32px;
font-weight: 700;
color: #666;
line-height: 28px
}
.counter-box.colored {
background: #eab736;
}
.counter-box.colored p,
.counter-box.colored .counter {
color: #fff;
}
<div class="container">
<div class="row contatore">
<div class="col-md-4">
<div class="counter-box colored">
<span class="counter value" akhi="560">0</span>
<p>Countries visited</p>
</div>
</div>
<div class="col-md-4">
<div class="counter-box">
<span class="counter value" akhi="3275">0</span>
<p>Registered travellers</p>
</div>
</div>
<div class="col-md-4">
<div class="counter-box">
<span class="counter value" id="conta" akhi="289">0</span>
<p>Partners</p>
</div>
</div>
</div>
</div>
What I have tried
i had tried with
const target = document.querySelector('.counter');
observer.observe(target);
but it doesn't seem to work. Many thanks to whoever can help me.
I would recommend, as others have suggested, to use the Intersection Observer API to animate your elements once they appear in the viewport.
The idea is simple, we'll create an observer that will observe the counters to animate and we're going to configure it so that it calls the animate function once a counter is fully visible in the viewport.
You may learn more about the options that an IntersectionObserver can accept in order to customize its behavior. Meanwhile, here's a live demo that illustrates how to make the counters animate once they appear in the screen (the code below has some helpful comments):
const counters = document.querySelectorAll('.value'),
speed = 400,
/**
* create an IntersectionObserver with the specified callback that will be executed for each intersection change for every counter we have.
* You may customize the options (2nd argument) per you requirement
*/
observer = new IntersectionObserver(
entries => entries.forEach(entry => entry.isIntersecting && animate(entry.target)),
{
threshold: 1 // tells the browser that we only need to execute the callback only when an element (counter) is fully visible in the viewport
}
),
// the animate function now accepts a counter (HTML element)
animate = counter => {
const value = +counter.dataset.akhi,
data = +counter.innerText,
time = value / speed;
if (data < value) {
counter.innerText = Math.ceil(data + time);
setTimeout(() => animate(counter), 1);
} else {
counter.innerText = value;
}
};
// attach the counters to the observer
counters.forEach(c => observer.observe(c));
.counter-box {
display: block;
background: #f6f6f6;
padding: 40px 20px 37px;
text-align: center
}
.counter-box p {
margin: 5px 0 0;
padding: 0;
color: #909090;
font-size: 18px;
font-weight: 500
}
.counter {
display: block;
font-size: 32px;
font-weight: 700;
color: #666;
line-height: 28px
}
.counter-box.colored {
background: #eab736;
}
.counter-box.colored p,
.counter-box.colored .counter {
color: #fff;
}
<div class="container">
<div class="row contatore">
<div class="col-md-4">
<div class="counter-box colored">
<!-- it is recommended to use "data-*" attributes to cache data that we might use later. The "data-akhi" contains the number to animate -->
<span class="counter value" data-akhi="560">0</span>
<p>Countries visited</p>
</div>
</div>
<div class="col-md-4">
<div class="counter-box">
<span class="counter value" data-akhi="3275">0</span>
<p>Registered travellers</p>
</div>
</div>
<div class="col-md-4">
<div class="counter-box">
<span class="counter value" id="conta" data-akhi="289">0</span>
<p>Partners</p>
</div>
</div>
</div>
</div>
As others suggested, you should use Intersection Observer.
This is how I'd do:
Scrolldown the snippet in order to see the counter animating up once is on the screen.
const counters = document.querySelectorAll('.value');
const speed = 400;
const observer = new IntersectionObserver( items => {
if(items[0].isIntersecting) {
const target = items[0].target;
const animate = () => {
const value = + target.getAttribute('akhi');
const data = + target.innerText;
const time = value / speed;
if(data < value) {
target.innerText = Math.ceil(data + time);
setTimeout(animate, 1);
}else{
target.innerText = value;
}
}
animate();
observer.unobserve(target);
}
})
counters.forEach( counter => observer.observe(counter));
.counter-box {
display: block;
background: #f6f6f6;
padding: 40px 20px 37px;
text-align: center
}
.counter-box p {
margin: 5px 0 0;
padding: 0;
color: #909090;
font-size: 18px;
font-weight: 500
}
.counter {
display: block;
font-size: 32px;
font-weight: 700;
color: #666;
line-height: 28px
}
.counter-box.colored {
background: #eab736;
}
.counter-box.colored p,
.counter-box.colored .counter {
color: #fff;
}
<div style="height: 600px;">
</div>
<div class="container">
<div class="row contatore">
<div class="col-md-4">
<div class="counter-box colored">
<span class="counter value" akhi="560">0</span>
<p>Countries visited</p>
</div>
</div>
<div class="col-md-4">
<div class="counter-box">
<span class="counter value" akhi="3275">0</span>
<p>Registered travellers</p>
</div>
</div>
<div class="col-md-4">
<div class="counter-box">
<span class="counter value" id="conta" akhi="289">0</span>
<p>Partners</p>
</div>
</div>
</div>
</div>

CSS Partial Scroll Snapping

Been playing around with the scroll snapping, looks like it saves a lot of head scratching with writing the functionality in JS.
Here's a challenge, has anyone out there found a way to selectively choose which children to snap and which children to freely scroll?
I think this will be useful for content rich pages that contain parts that wouldn't benefit from scroll snapping.
Here's an example of the problem:
https://codepen.io/nodelondon/pen/YzxWqLG
html {
background: #f2f2f2;
}
.scroll-container,
.scroll-area-none,
.scroll-area {
max-width: 850px;
height: 600px;
font-size: 60px;
}
.scroll-area-none {
scroll-snap-align: none;
background-color: black;
}
.scroll-container {
overflow: auto;
scroll-snap-type: y mandatory;
}
.scroll-area {
scroll-snap-align: start;
}
.scroll-container,
.scroll-area-none,
.scroll-area {
margin: 0 auto;
}
.scroll-area-none,
.scroll-area {
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.scroll-area:nth-of-type(4n+1) {
background: #49b293;
}
.scroll-area:nth-of-type(4n+2) {
background: #c94e4b;
}
.scroll-area:nth-of-type(4n+3) {
background: #4cc1be;
}
.scroll-area:nth-of-type(4n+4) {
background: #8360A6;
}
<div class="support-scrollsnap"></div>
<div class="scroll-container">
<div class="scroll-area-none">-1</div>
<div class="scroll-area-none">0</div>
<div class="scroll-area">1</div>
<div class="scroll-area">2</div>
<div class="scroll-area">3</div>
<div class="scroll-area">4</div>
<div class="scroll-area-none">5</div>
<div class="scroll-area-none">6</div>
</div>
Ideally, the boxes with -1,0,5 and 6 should be able to freely scroll but the mandatory boxes in between keep pulling you back.
If you're thinking of suggesting changing it to proximity, this is a good suggestion, but, with IOS Safari (On OSX Safari for me as well), unfortunately it still forces scroll snapping on boxes that have scroll-snap-align set to Start no matter where you are on the page.
Found that changing scroll-snap-type on documentElement causes the scroll to jump to the nearest snap-align element, which seems to look awful.
Looks like the simplest fix is working fine:
let t = window.scrollY;
requestAnimationFrame(() => window.scroll(0, t));
I propose the following logic:
Define which children block is at the top border of the scroll container. I am using the Element.getBoundingClientRect() method to compare positions of the scroll container and its children.
Check which scroll-snap-align property has this child block.
Set the scroll-snap-type property of the container as y proximity or y mandatory.
Handle the scroll event on desktop, On mobile this event works at the end of the scroll, so we need something else for mobile (may be jQuery Mobile).
Here is a working draft solution. But it requires optimizations and improvements such as scroll event throttling.
https://codepen.io/glebkema/pen/zYdPqeY
let scrollContainers = document.getElementsByClassName('scroll-container');
for (let sc of scrollContainers) {
sc.addEventListener('scroll', updateSnapType);
sc.addEventListener('touchstart', updateSnapType);
}
function updateSnapType(event) {
let parent = event.currentTarget;
let parentRect = parent.getBoundingClientRect();
for (let child of parent.children) {
let childRect = child.getBoundingClientRect();
if (childRect.top <= parentRect.top && parentRect.top < childRect.bottom) {
let childStyle = window.getComputedStyle(child);
let scrollSnapAlign = childStyle.getPropertyValue('scroll-snap-align');
console.log(child.innerText, scrollSnapAlign);
parent.style.scrollSnapType = "none" === scrollSnapAlign ? 'y proximity' : 'y mandatory';
break;
}
}
}
html {
background: #f2f2f2;
}
.scroll-container,
.scroll-area-none,
.scroll-area {
height: 100px;
font-size: 60px;
}
.scroll-container {
max-width: 850px;
margin: 15px auto;
overflow: auto;
scroll-snap-type: y mandatory;
}
.scroll-area {
scroll-snap-align: start;
}
.scroll-area-none {
scroll-snap-align: none;
background-color: black;
}
.scroll-area-none,
.scroll-area {
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.scroll-area:nth-of-type(4n+1) {
background: #49b293;
}
.scroll-area:nth-of-type(4n+2) {
background: #c94e4b;
}
.scroll-area:nth-of-type(4n+3) {
background: #4cc1be;
}
.scroll-area:nth-of-type(4n+4) {
background: #8360A6;
}
<div class="scroll-container">
<div class="scroll-area-none">1. -1</div>
<div class="scroll-area-none">1. 0</div>
<div class="scroll-area">1. 1</div>
<div class="scroll-area">1. 2</div>
<div class="scroll-area">1. 3</div>
<div class="scroll-area">1. 4</div>
<div class="scroll-area-none">1. 5</div>
<div class="scroll-area-none">1. 6</div>
</div>
<div class="scroll-container">
<div class="scroll-area-none">2. -1</div>
<div class="scroll-area-none">2. 0</div>
<div class="scroll-area">2. 1</div>
<div class="scroll-area">2. 2</div>
<div class="scroll-area">2. 3</div>
<div class="scroll-area">2. 4</div>
<div class="scroll-area-none">2. 5</div>
<div class="scroll-area-none">2. 6</div>
</div>

How to check if an element is clicked

<div class="game">
<div class="hole hole1">
<div class="mole"></div>
</div>
<div class="hole hole2">
<div class="mole"></div>
</div>
<div class="hole hole3">
<div class="mole"></div>
</div>
<div class="hole hole4">
<div class="mole"></div>
</div>
<div class="hole hole5">
<div class="mole"></div>
</div>
<div class="hole hole6">
<div class="mole"></div>
</div>
<div class="hole hole7">
<div class="mole"></div>
</div>
<div class="hole hole8">
<div class="mole"></div>
</div>
<div class="hole hole9">
<div class="mole"></div>
</div>
</div>
.game {
width: 300px;
height: 600px;
display: flex;
flex-wrap: wrap;
margin: 0 auto;
}
.hole {
flex: 1 0 33.33%;
overflow: hidden;
width: 100%;
position: relative;
}
.hole:after {
display: block;
background: url('gaura1.png') bottom center no-repeat;
background-size: 100%;
content: '';
width: 100%;
height:100%;
position: absolute;
z-index: 2;
}
.mole {
background: url('flowAlb.png') bottom center no-repeat;
background-size: 100%;
position: absolute;
width: 100%;
height:100%;
top:100%;
transition: all 0.4s;
}
.hole.up .mole {
top: -20px;
z-index:3;
}
I'm trying to learn web development with CSS, HTML and JAVASCRIPT(i'm new to all of them) and I'm working on some whack-a-mole game code that was free to work with. I understood what the code does, but I'm not sure how to work with. I have an event listener on click for each mole, but I wanna do something if the mole isn't clicked and I don't know how to check that. I tried different methods, using boolean variables or trying to check if the click was outside the element, but none of them worked. I'm pretty sure I didn't used them right, so I would really appreciate some help or information. I'll leave here the JS code. Thanks so much!
const holes = document.querySelectorAll('.hole');
const scoreBoard = document.querySelector('.score');
const moles = document.querySelectorAll('.mole');
const mySound=document.getElementById("sound");
const joc=document.getElementsByClassName("game");
let lastHole;
let timeUp = false;
let score = 0;
function randomTime(min, max) {
return Math.round(Math.random() * (max - min) + min);
}
function randomHole(holes) {
const index = Math.floor(Math.random() * holes.length);
const hole = holes[index];
if (hole === lastHole) {
return randomHole(holes);
}
lastHole = hole;
return hole;
}
function peep() {
const time = randomTime(500, 1000);
const hole = randomHole(holes);
hole.classList.add('up');
setTimeout(() => {
hole.classList.remove('up');
if (!timeUp) {
peep();
}
}, time);
}
function startGame() {
scoreBoard.textContent = 0;
timeUp = false;
score = 0;
peep();
setTimeout(() => timeUp = true, 90000)
}
function wack(e) {
if (!e.isTrusted) return;
score = score + 100;
this.parentNode.classList.remove('up');
scoreBoard.textContent = score;
}
moles.forEach(mole => mole.addEventListener('click', wack));
You can add a click event to the documentElement and check its target. Consequently you could also remove your other click handlers while doing this and check for the class mole.
;document.documentElement.onclick = function(event){
//REM: Target element of the click
var tTarget = event.srcElement || event.target;
//REM: Check which element was targted.
if(tTarget.id === 'sample'){
alert('You clicked on #sample')
}
else{
alert('You clicked on "' + tTarget.tagName + '"')
}
};
html, body{
height: 100%;
position: relative;
width: 100%
}
body{
background: yellow
}
div{
background: red
}
.sample{
background: lime;
height: 100%;
position: relative;
width: 100%
}
<div class = 'sample'>
<div id = 'sample'>click me or not</div>
</div>

Adapt vanilla js carousel to ie

I'm building a website that uses a carousel like this one:
https://codepen.io/queflojera/pen/RwwLbEY?editors=1010
It works perfectly on opera, chrome, edge but it stops working on ie and I need it to work on ie as well, if anyone knows any way around I'll really appreciate it.
//I'm not pretty sure what is causing the ie failure on this code
// Select the carousel you'll need to manipulate and the buttons you'll add events to
const carousel = document.querySelector("[data-target='carousel']");
const card = carousel.querySelector("[data-target='card']");
const leftButton = document.querySelector("[data-action='slideLeft']");
const rightButton = document.querySelector("[data-action='slideRight']");
// Prepare to limit the direction in which the carousel can slide,
// and to control how much the carousel advances by each time.
// In order to slide the carousel so that only three cards are perfectly visible each time,
// you need to know the carousel width, and the margin placed on a given card in the carousel
const carouselWidth = carousel.offsetWidth;
const cardStyle = card.currentStyle || window.getComputedStyle(card)
const cardMarginRight = Number(cardStyle.marginRight.match(/\d+/g)[0]);
// Count the number of total cards you have
const cardCount = carousel.querySelectorAll("[data-target='card']").length;
// Define an offset property to dynamically update by clicking the button controls
// as well as a maxX property so the carousel knows when to stop at the upper limit
let offset = 0;
const maxX = -((cardCount) * carouselWidth +
(cardMarginRight * cardCount) -
carouselWidth - cardMarginRight);
// Add the click events
leftButton.addEventListener("click", function() {
if (offset !== 0) {
offset += carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
})
rightButton.addEventListener("click", function() {
if (offset !== maxX) {
offset -= carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
})
.wrapper {
height: 200px;
width: 632px;
position: relative;
overflow: hidden;
margin: 0 auto;
}
.button-wrapper {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
}
.carousel {
margin: 0;
padding: 0;
list-style: none;
width: 100%;
display: flex;
position: absolute;
left: 0;
transition: all .5s ease;
}
.card {
background: black;
min-width: 632px;
height: 200px;
display: inline-block;
}
.card:nth-child(odd) {
background-color: blue;
}
.card:nth-child(even) {
background-color: red;
}
<div class="wrapper">
<ul class="carousel" data-target="carousel">
<li class="card" data-target="card">1</li>
<li class="card" data-target="card">2</li>
<li class="card" data-target="card">3</li>
<li class="card" data-target="card">4</li>
<li class="card" data-target="card">5</li>
<li class="card" data-target="card">6</li>
<li class="card" data-target="card">7</li>
<li class="card" data-target="card">8</li>
<li class="card" data-target="card">9</li>
</ul>
<div class="button-wrapper">
<button data-action="slideLeft">L</button>
<button data-action="slideRight">R</button>
</div>
</div>
Invalid character
carousel.style.transform = `translateX(${offset}px)`;
IE does not support template literals (backticks)
To fix use
carousel.style.transform = "translateX("+offset+"px)";
Also getting
Unable to get property '0' of undefined or null reference
because it is auto in IE
const cardMarginRight = Number(cardStyle.marginRight.match(/\d+/g)[0]);
Fix:
const marginRight = cardStyle.marginRight;
const cardMarginRight = isNaN(parseInt(marginRight)) ? 0 : Number(cardStyle.marginRight.match(/\d+/g)[0]);
//I'm not pretty sure what is causing the ie failure on this code
// Select the carousel you'll need to manipulate and the buttons you'll add events to
const carousel = document.querySelector("[data-target='carousel']");
const card = carousel.querySelector("[data-target='card']");
const leftButton = document.querySelector("[data-action='slideLeft']");
const rightButton = document.querySelector("[data-action='slideRight']");
// Prepare to limit the direction in which the carousel can slide,
// and to control how much the carousel advances by each time.
// In order to slide the carousel so that only three cards are perfectly visible each time,
// you need to know the carousel width, and the margin placed on a given card in the carousel
const carouselWidth = carousel.offsetWidth;
const cardStyle = card.currentStyle || window.getComputedStyle(card)
const marginRight = cardStyle.marginRight;
const cardMarginRight = isNaN(parseInt(marginRight)) ? 0 : Number(cardStyle.marginRight.match(/\d+/g)[0]);
// Count the number of total cards you have
const cardCount = carousel.querySelectorAll("[data-target='card']").length;
// Define an offset property to dynamically update by clicking the button controls
// as well as a maxX property so the carousel knows when to stop at the upper limit
let offset = 0;
const maxX = -((cardCount) * carouselWidth +
(cardMarginRight * cardCount) -
carouselWidth - cardMarginRight);
// Add the click events
leftButton.addEventListener("click", function() {
if (offset !== 0) {
offset += carouselWidth + cardMarginRight;
carousel.style.transform = "translateX("+offset+"px)";
}
})
rightButton.addEventListener("click", function() {
if (offset !== maxX) {
offset -= carouselWidth + cardMarginRight;
carousel.style.transform = "translateX("+offset+"px)";
}
})
.wrapper {
height: 200px;
width: 632px;
position: relative;
overflow: hidden;
margin: 0 auto;
}
.button-wrapper {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
}
.carousel {
margin: 0;
padding: 0;
list-style: none;
width: 100%;
display: flex;
position: absolute;
left: 0;
transition: all .5s ease;
}
.card {
background: black;
min-width: 632px;
height: 200px;
display: inline-block;
}
.card:nth-child(odd) {
background-color: blue;
}
.card:nth-child(even) {
background-color: red;
}
<div class="wrapper">
<ul class="carousel" data-target="carousel">
<li class="card" data-target="card">1</li>
<li class="card" data-target="card">2</li>
<li class="card" data-target="card">3</li>
<li class="card" data-target="card">4</li>
<li class="card" data-target="card">5</li>
<li class="card" data-target="card">6</li>
<li class="card" data-target="card">7</li>
<li class="card" data-target="card">8</li>
<li class="card" data-target="card">9</li>
</ul>
<div class="button-wrapper">
<button data-action="slideLeft">L</button>
<button data-action="slideRight">R</button>
</div>
</div>

Show only a few users and hide others

Can anyone explain how to make a user list like as shown in the image below...
I'm making a project in Meteor and using Materialize for template and I want to display the list of assigned users. If there are more than a particular count(say 5) of users i want them to be displayed like on that image... I have tried googling this and haven't found anything useful. I also checked the Materialize website and found nothing useful. So if anyone has an idea please help share it.
Ok so this is the output html, in this case i only have one member but in real case I will have more:
<div class="row"> ==$0
<label class="active members_padding_card_view">Members</label>
<div class="toolBarUsers flex" style="float:right;">
<dic class="other-profile" style="background-color:#f06292;">
<span>B</span>
</div>
This is the .js code
Template.profile.helpers({
randomInitials: function () {
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var nLetter = chars.charAt(Math.floor(Math.random()*chars.length));
var sLetter = chars.charAt(Math.floor(Math.random()*chars.length));
return nLetter + sLetter;
},
tagColor: function () {
var colors = ["#e57373","#f06292","#ba68c8","#9575cd","#7986cb","#64b5f6","#4fc3f7","#4dd0e1","#4db6ac","#81c784","#aed581","#dce775","#fff176","#ffd54f","#ffb74d","#ff8a65","#a1887f","#e0e0e0","#90a4ae"];
return colors[Math.floor(Math.random()*colors.length)];
},
randomAllowed : function(possible) {
var count = Math.floor((Math.random() * possible) + 1);
if(count == 1) {
return;
}
return "none";
},
membersList() {
const instance = Template.instance();
const cardDataId = new Mongo.ObjectID(instance.data.cardData._id.valueOf());
return CardDataMembers.find({lkp_card_data_fkeyi_ref: cardDataId});
},
memberData: function() {
// We use this helper inside the {{#each posts}} loop, so the context
// will be a post object. Thus, we can use this.xxxx from above memberList
return Meteor.users.findOne(this.lkp_user_fkeyi_ref);
},
showMembers() {
const instance = Template.instance();
const cardDataId = new Mongo.ObjectID(instance.data.cardData._id.valueOf());
let membersCount = CardDataMembers.find({lkp_card_data_fkeyi_ref: cardDataId}).count();
////console.log(membersCount);
if (membersCount > 0) {
$('.modal-trigger').leanModal();
return true;
} else {
return false;
}
},
});
Right now if I add a lot of users I get this:
This can be done in many ways, but I've used CSS Flexbox.
I've used two <div>s one contains single user circles having class .each-user that is expanding (for reference I've taken 6) and another contains the total number of users having class .total-users.
It's a bit confusing but if you look into my code below or see this Codepen you'll get to know everything.
html, body {
width: 100%;
height: 100%;
margin: 0;
font-family: Roboto;
}
.container {
display: flex;
align-content: center;
justify-content: center;
margin-top: 20px;
}
/* Contains all the circles */
.users-holder {
display: flex;
}
/* Contains all circles (those without total value written on it) */
.each-user {
display: flex;
flex-wrap: wrap;
padding: 0 10px;
max-width: 300px;
height: 50px;
overflow: hidden;
}
/* Circle Styling */
.circle {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 10px;
}
.each-user .circle {
background: #00BCD4;
}
.each-user .circle:last-child {
margin-right: 0;
}
/* Circle showing total */
.total-users {
padding: 0;
margin-bottom:
}
.total-users .circle {
background: #3F51B5;
margin: 0;
position: relative;
}
.total-users .circle .txt {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
}
<div class="container">
<div class="users-holder">
<div class="total-users">
<div class="circle">
<span class="txt">+12</span>
</div>
</div>
<div class="each-user">
<div class="circle user-circle"></div>
<div class="circle user-circle"></div>
<div class="circle user-circle"></div>
<div class="circle user-circle"></div>
<div class="circle user-circle"></div>
<!-- Sixth Circle -->
<div class="circle"></div>
</div>
</div>
</div>
Hope this helps!
I've used jQuery. See this https://jsfiddle.net/q86x7mjh/26/
HTML:
<div class="user-list-container">
<div class="total-circle hidden"><span></span></div>
<div class="user-circle"><span>T</span></div>
<div class="user-circle"><span>C</span></div>
<div class="user-circle"><span>U</span></div>
<div class="user-circle"><span>M</span></div>
<div class="user-circle"><span>R</span></div>
<div class="user-circle"><span>Z</span></div>
<div class="user-circle"><span>N</span></div>
<div class="user-circle"><span>O</span></div>
<div class="user-circle"><span>M</span></div>
<div>
jQuery:
var items_to_show = 5;
if($('.user-circle').length > items_to_show){
var hide = $('.user-circle').length - items_to_show;
for(var i = 0; i < hide; i++){
$('.user-circle').eq(i).addClass('hidden');
}
$('.total-circle').removeClass('hidden');
$('.total-circle span').text('+' + hide);
}
So after quite some time I have solved the problem. I am posting my answer here for anyone that will in the future experience a similar issue...
Have a good day!
I have added the following lines of code to my template:
return CardDataMembers.find({lkp_card_data_fkeyi_ref: cardDataId},{sort: {createdAt: -1}, limit: 3});
diffMembers(){
const instance = Template.instance();
const cardDataId = new Mongo.ObjectID(instance.data.cardData._id.valueOf());
const limit = 3;
const allMembersOnCard = CardDataMembers.find({lkp_card_data_fkeyi_ref: cardDataId}).count();
let remainingMembers = allMembersOnCard - limit;
return remainingMembers;
},
And in the HTML included:
<div class="other-profile" style="background-color:#dedede;">
<span>+{{diffMembers}}</span>
</div>

Categories