jQuery animate number function. Want to turn it into Vanilla JavaScript - javascript

this function animates number inside an element to a defined number inside data-count value
How could I please do it in vanilla JavaScript
<div class="counter">
<div class="row no-gutters">
<div class="col-4">
<div
class="single-counter counter-color-1 d-flex align-items-center justify-content-center"
>
<div class="counter-items text-center">
<span id="count" data-count="175">0</span
><span>K</span>
<p>Downloads</p>
</div>
</div>
</div>
<div class="col-4">
<div
class="single-counter counter-color-2 d-flex align-items-center justify-content-center"
>
<div class="counter-items text-center">
<span id="count" data-count="73">0</span
><span>K</span>
<p>Active users</p>
</div>
</div>
</div>
<div class="col-4">
<div
class="single-counter counter-color-3 d-flex align-items-center justify-content-center"
>
<div class="counter-items text-center">
<span id="count" data-count="4.8">0</span>
<p>user rating</p>
</div>
</div>
</div>
</div>
$('#count').each(function() {
var counter = $(this),
countTo = counter.attr('data-count');
const countObj = { countNum: counter.text()}
$(countObj).animate({
countNum: countTo
},{
duration: 2000,
easing:'linear',
step: function() {
counter.text(Math.floor(this.countNum));
},
complete: function() {
counter.text(this.countNum);
}
});
});
I tried this
countUp(elem) {
var current = elem.innerHTML;
var interval = setInterval(increase, 70);
function increase() {
elem.innerHTML = current++;
if (current > elem.getAttribute("data-count")) {
clearInterval(interval);
}
}
}
var span = document.querySelectorAll("#count");
var i = 0;
for (i; i < span.length; i++) {
countUp(span[i]);
}
but it doesn't finish all elements animation at the same time; the elements which has the lower data-count value finishes earlier than the others that have higher data-count value

element is not a selector in your case. Another issue is all <span> have duplicate id i.e. count
I have modified these duplicate ids to count1, count2, count3. And selector $('span[id^=count') in script below means all <span> elements which have id starting with word count
$('span[id^=count').each(function() {
var counter = $(this),
countTo = counter.attr('data-count');
const countObj = { countNum: counter.text()}
$(countObj).animate({
countNum: countTo
},
{
duration: 2000,
easing:'linear',
step: function() {
counter.text(Math.floor(this.countNum));
},
complete: function() {
counter.text(this.countNum);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<div class="counter">
<div class="row no-gutters">
<div class="col-4">
<div class="single-counter counter-color-1 d-flex align-items-center justify-content-center">
<div class="counter-items text-center">
<span id="count1" data-count="175">0</span><span>K</span>
<p>Downloads</p>
</div>
</div>
</div>
<div class="col-4">
<div class="single-counter counter-color-2 d-flex align-items-center justify-content-center">
<div class="counter-items text-center">
<span id="count2" data-count="73">0</span><span>K</span>
<p>Active users</p>
</div>
</div>
</div>
<div class="col-4">
<div class="single-counter counter-color-3 d-flex align-items-center justify-content-center">
<div class="counter-items text-center">
<span id="count3" data-count="4.8">0</span>
<p>user rating</p>
</div>
</div>
</div>
</div>
EDIT : Below is pure vanilla js function for you
You just need some basic maths to decide time interval for all elements
function countUp(elem) {
var current = elem.innerHTML;
// assume 2000(milliseconds) is time delay to complete all animations
// determine time interval based on value of data-count
var timeIntervalBeforeIncrement = 2000/elem.getAttribute("data-count")
var interval = setInterval(increase, timeIntervalBeforeIncrement);
function increase() {
elem.innerHTML = current++;
if (current > elem.getAttribute("data-count")) {
clearInterval(interval);
}
}
}
var span = document.querySelectorAll("[id^='count']");
for (i = 0; i < span.length; i++) {
countUp(span[i]);
}

Related

How do I get this div to show again using JavaScript

I have made a TODO app and added a counter to keep a count of the items in the list. If the counter hits zero, I've set it to re-show a message 'You currently have no tasks. Use the input field above to start adding.'
if(count === 0){
noTasksText.classList.remove('d-none');
}
In the console I print out the div and it doesn't have d-none in the class list any more which is what I want, however, in the actual DOM it does.
Here is a full example - https://codepen.io/tomdurkin/pen/LYdpXKJ?editors=1111
I really can't seem to work this out. I can't seem to interact with that div when the counter becomes zero, however I can get console logs etc to show when expected.
Any help would be appreciated!
const mainInput = document.querySelector('#main-input');
const todoContainer = document.querySelector('#todo-container');
const errorText = document.querySelector('#js-error');
const noTasksText = document.querySelector('.js-no-tasks')
let tasks = [];
let count = 0;
// focus input on load
window.onload = () => {
mainInput.focus();
const storedTasks = JSON.parse(localStorage.getItem('tasks'));
if (storedTasks != null && storedTasks.length > 0) {
// set count to number of pre-existing items
count = storedTasks.length
// hide the 'no tasks' text
noTasksText.classList.add('d-none');
// overwrite tasks array with stored tasks
tasks = storedTasks;
tasks.forEach(task => {
// Build the markup
const markup = `
<div class="js-single-task single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${task}">${task}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// Append it to the container
todoContainer.innerHTML += markup;
});
} else {
if (noTasksText.classList.contains('d-none')) {
noTasksText.classList.remove('d-none');
}
}
};
// event listener for 'enter on input'
mainInput.addEventListener("keydown", e => {
// if error is showing, hide it!
if (!errorText.classList.contains('d-none')) {
errorText.classList.add('d-none');
}
if (e.key === "Enter") {
// Get the value of the input
let inputValue = mainInput.value;
if (inputValue) {
// Build the markup
const markup = `
<div class="js-single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${inputValue}">${inputValue}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// hide 'no tasks' text
noTasksText.classList.add('d-none');
// Append it to the container
todoContainer.innerHTML += markup;
// Push value to 'tasks' array
tasks.push(inputValue);
// Put in localStorage
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// Reset the value of the input field
mainInput.value = '';
// add 1 to the count
count++
} else {
// Some very basic validation
errorText.classList.remove('d-none');
}
}
});
// remove task
todoContainer.addEventListener('click', (e) => {
// Find the button in the row that needs removing (bubbling)
const buttonIsDelete = e.target.classList.contains('js-remove-task');
if (buttonIsDelete) {
// Remove the HTML from the screen
e.target.closest('.js-single-task').remove();
// Grab the name of the single task
let taskName = e.target.closest('.js-single-task').querySelector('.js-single-task-name h5').getAttribute('data-title');
// filter out the selected word
tasks = tasks.filter(item => item != taskName);
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// update counter
count--
// check if counter is zero and re-show 'no tasks' text if true
if (count === 0) {
noTasksText.classList.remove('d-none');
console.log(noTasksText);
}
}
});
body {
background: #e1e1e1;
}
<div class="container">
<div class="row d-flex justify-content-center mt-5">
<div class="col-10 col-lg-6">
<div class="card p-3">
<h2>To dos</h2>
<p>
Use this app to keep a list of things you need to do
</p>
<input class="form-control" id="main-input" type="text" placeholder="Type your todo and hit enter..." class="w-100" />
<small id="js-error" class="text-danger d-none">
Please type a value and press enter
</small>
<hr />
<h4 class="mb-5">Your 'To dos'</h4>
<div id="todo-container">
<!-- todos append in here -->
<div class="js-no-tasks">
<small class="d-block w-100 text-center mb-3">
<i>
You currently have no tasks. Use the input field above to start adding
</i>
</small>
</div>
</div>
</div>
<!-- /card -->
</div>
</div>
</div>
Upon setting innerHTML by using += innerHTML the node noTasksText is lost, because browser processes the whole new set innerHTML and creates new objects. You can either retrieve noTasksText again after that, or append nodes using todoContainer.appendChild. I forked your pen and solved it with the latter solution.
https://codepen.io/aghosey/pen/wvmGwWd
You can do the following, it will work (here innerHTML is changing the DOM, so I added an extra function to recalculate elements after DOM is changed due to innerHTML):
var mainInput = document.querySelector("#main-input");
var todoContainer = document.querySelector("#todo-container");
var errorText = document.querySelector("#js-error");
var noTasksText = document.querySelector(".js-no-tasks");
let tasks = [];
let count = 0;
function getAllElements() {
mainInput = document.querySelector("#main-input");
todoContainer = document.querySelector("#todo-container");
errorText = document.querySelector("#js-error");
noTasksText = document.querySelector(".js-no-tasks");
}
// focus input on load
window.onload = () => {
mainInput.focus();
var storedTasks = JSON.parse(localStorage.getItem("tasks"));
if (storedTasks != null && storedTasks.length > 0) {
// set count to number of pre-existing items
count = storedTasks.length;
// hide the 'no tasks' text
noTasksText.classList.add("d-none");
// overwrite tasks array with stored tasks
tasks = storedTasks;
tasks.forEach((task) => {
// Build the markup
const markup = `
<div class="js-single-task single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${task}">${task}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// Append it to the container
todoContainer.innerHTML += markup;
getAllElements();
});
} else {
if (noTasksText.classList.contains("d-none")) {
noTasksText.classList.remove("d-none");
}
}
};
// event listener for 'enter on input'
mainInput.addEventListener("keydown", (e) => {
// if error is showing, hide it!
if (!errorText.classList.contains("d-none")) {
errorText.classList.add("d-none");
}
if (e.key === "Enter") {
// Get the value of the input
let inputValue = mainInput.value;
if (inputValue) {
// Build the markup
const markup = `
<div class="js-single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${inputValue}">${inputValue}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// hide 'no tasks' text
noTasksText.classList.add("d-none");
// Append it to the container
todoContainer.innerHTML += markup;
getAllElements();
// Push value to 'tasks' array
tasks.push(inputValue);
// Put in localStorage
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// Reset the value of the input field
mainInput.value = "";
// add 1 to the count
count++;
} else {
// Some very basic validation
errorText.classList.remove("d-none");
}
}
});
// remove task
todoContainer.addEventListener("click", (e) => {
// Find the button in the row that needs removing (bubbling)
const buttonIsDelete = e.target.classList.contains("js-remove-task");
if (buttonIsDelete) {
// Remove the HTML from the screen
e.target.closest(".js-single-task").remove();
// Grab the name of the single task
let taskName = e.target
.closest(".js-single-task")
.querySelector(".js-single-task-name h5")
.getAttribute("data-title");
// filter out the selected word
tasks = tasks.filter((item) => item != taskName);
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// update counter
count--;
// check if counter is zero and re-show 'no tasks' text if true
if (count === 0) {
noTasksText.classList.remove("d-none");
console.log(noTasksText);
}
}
});
body {
background: #e1e1e1;
}
<div class="container">
<div class="row d-flex justify-content-center mt-5">
<div class="col-10 col-lg-6">
<div class="card p-3">
<h2>To dos</h2>
<p>
Use this app to keep a list of things you need to do
</p>
<input class="form-control" id="main-input" type="text" placeholder="Type your todo and hit enter..." class="w-100" />
<small id="js-error" class="text-danger d-none">
Please type a value and press enter
</small>
<hr />
<h4 class="mb-5">Your 'To dos'</h4>
<div id="todo-container">
<!-- todos append in here -->
<div class="js-no-tasks">
<small class="d-block w-100 text-center mb-3">
<i>
You currently have no tasks. Use the input field above to start adding
</i>
</small>
</div>
</div>
</div>
<!-- /card -->
</div>
</div>
</div>

Automate a slider with pause and resume functions

I have a slider and I would like to automate it with pause and resume functions. I found this great snippet of code that allows me to pause and resume, however, it only allows me to pause once.
I am struggling to figure out how to make it so the user can pause as many times as they like.
here is the complete code:
$s = 1000; // slide transition speed (for sliding carousel)
$d = 5000; // duration per slide
$w = $('.slide').width(); // slide width
function IntervalTimer(callback, interval) {
var timerId, startTime, remaining = 0;
var state = 0; // 0 = idle, 1 = running, 2 = paused, 3= resumed
this.pause = function () {
if (state != 1) return;
remaining = interval - (new Date() - startTime);
window.clearInterval(timerId);
state = 2;
$('.timer').stop(true, false)
};
this.resume = function () {
if (state != 2) return;
state = 3;
window.setTimeout(this.timeoutCallback, remaining);
$('.timer').animate({"width":$w}, remaining);
$('.timer').animate({"width":0}, 0);
};
this.timeoutCallback = function () {
if (state != 3) return;
callback();
startTime = new Date();
timerId = window.setInterval(callback, interval);
state = 1;
};
startTime = new Date();
timerId = window.setInterval(callback, interval);
state = 1;
}
var starttimer = new IntervalTimer(function () {
autoSlider()
}, $d);
timer();
$('.slider-content').hover(function(ev){
starttimer.pause()
}, function(ev){
starttimer.resume()
});
function timer() {
$('.timer').animate({"width":$w}, $d);
$('.timer').animate({"width":0}, 0);
}
Any help is greatly appreciated.
UPDATE:
Here is the HTML
<div class="slider-content">
<div class="timer"></div>
<div class="slide 1">
<div class="hero-container">
<div class="header-content-container">
<h1 class="entry-title"></h1>
<hr>
<div class="flex-container">
<div class="col1"></div>
<div class="col2"></div>
</div>
<div class="post-link"></div>
</div>
</div>
</div>
<div class="slide 2">
<div class="hero-container">
<div class="header-content-container">
<h1 class="entry-title"></h1>
<hr>
<div class="flex-container">
<div class="col1"></div>
<div class="col2"></div>
</div>
<div class="post-link"></div>
</div>
</div>
</div>
<div class="slide 3">
<div class="hero-container">
<div class="header-content-container">
<h1 class="entry-title"></h1>
<hr>
<div class="flex-container">
<div class="col1"></div>
<div class="col2"></div>
</div>
<div class="post-link"></div>
</div>
</div>
</div>
<div class="slide 4">
<div class="hero-container">
<div class="header-content-container">
<h1 class="entry-title"></h1>
<hr>
<div class="flex-container">
<div class="col1"></div>
<div class="col2"></div>
</div>
<div class="post-link"></div>
</div>
</div>
</div>
</div>

Animated counters on scroll not all loading

I have wrote the code below to get animated counters starting when visible on the window. It works well when the counters are all visible on the same row, but if only the first one is visible, this one will start the animation, but the others won't even if we scroll down. The first one is complete, but the others remain to zero.
/* SCROLL FUNCTIONS */
// Every time the window is scrolled...
$(window).scroll(function() {
// Check the location of each desired element
$('.counter').each(function(i) {
var bottom_of_object = $(this).offset().top + $(this).outerHeight();
var bottom_of_window = $(window).scrollTop() + $(window).height();
// If the object is completely visible in the window, fade it it
if (bottom_of_window > bottom_of_object) {
var $this = $(this);
$({
Counter: 0
}).animate({
Counter: $this.attr('data-to')
}, {
duration: 2000,
easing: 'swing',
step: function() {
$this.text(Math.ceil(this.Counter));
},
complete() {
$this.text(Math.ceil(this.Counter));
}
});
$(window).off("scroll");
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="row">
<div class="col">
<div class="row counters text-dark">
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="counter" data-to="30000">0</div>
<label>Happy Clients</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="counter" data-to="15">0</div>
<label>Years in Business</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-sm-0">
<div class="counter" data-to="352">0</div>
<label>Cups of Coffee</label>
</div>
<div class="col-sm-6 col-lg-3">
<div class="counter" data-to="178">0</div>
<label>High Score</label>
</div>
</div>
</div>
</div>
The problem is this line of code:
$(window).off("scroll");
Your off call unbinds all events, not just one. That means all scroll event bindings are lost after the first number animation executes.
To solve this, you need to bind and unbind each number's animation separately. A simple way to do this would be to have a different function for each number animation and bind/unbind them separately. A generic example:
var myScroll1 = function () {
$(window).off("scroll", myScroll1)
}
$(window).on("scroll", myScroll1)
Notice we are turning on and off just this specific function reference. You can have 4 of them and switch them on and off separately.
EDIT: Here's your script modified to work as explained:
var anim1 = function () { animateAndKill(1, $("#n1"), 3000, anim1); }
var anim2 = function () { animateAndKill(2, $("#n2"), 15, anim2); }
var anim3 = function () { animateAndKill(3, $("#n3"), 352, anim3); }
var anim4 = function () { animateAndKill(4, $("#n4"), 178, anim4); }
// Every time the window is scrolled...
function animateAndKill(id, $number, max, myFunction) {
var bottom_of_object = $number.offset().top + $number.outerHeight();
var bottom_of_window = $(window).scrollTop() + window.innerHeight;
// If the object is completely visible in the window, fade it it
if (bottom_of_window > bottom_of_object) {
$({ Counter: 0 }).animate({ Counter: max }, {
duration: 2000,
easing: 'swing',
step: function () {
var n = Math.ceil(this.Counter);
$number.html(n);
}
});
$(window).off("scroll", myFunction);
}
}
$(window).on("scroll", anim1);
$(window).on("scroll", anim2);
$(window).on("scroll", anim3);
$(window).on("scroll", anim4);
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="code.js"></script>
</head>
<body>
<div style="height: 1000px; background: #33FF44"></div>
<div class="row" style="z-index: 100; font-size: 100px;">
<div class="col">
<div class="row counters text-dark">
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div id="n1" class="counter" data-to="30000">0</div>
<label>Happy Clients</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div id="n2" class="counter" data-to="15">0</div>
<label>Years in Business</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-sm-0">
<div id="n3" class="counter" data-to="352">0</div>
<label>Cups of Coffee</label>
</div>
<div class="col-sm-6 col-lg-3">
<div id="n4" class="counter" data-to="178">0</div>
<label>High Score</label>
</div>
</div>
</div>
</div>
<div style="height: 3000px; background: #33FF44"></div>
</body>
</html>
https://jsfiddle.net/tyddlywink/pdvh4b3n/
Get rid of the $(window).off("scroll");bit. And keep track of who's already been counted or not.
<div class="row">
<div class="col">
<div class="row counters text-dark">
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="counter" data-to="30000" data-counted='false'>0</div>
<label>Happy Clients</label>
</div>
<div style="height: 750px">
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="counter" data-to="15" data-counted='false'>0</div>
<label>Years in Business</label>
</div>
<div style="height: 750px">
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-sm-0">
<div class="counter" data-to="352" data-counted='false'>0</div>
<label>Cups of Coffee</label>
</div>
<div style="height: 750px">
</div>
<div class="col-sm-6 col-lg-3">
<div class="counter" data-to="178" data-counted='false'>0</div>
<label>High Score</label>
</div>
</div>
</div>
</div>
Javascript:
// Every time the window is scrolled...
$(window).scroll(function() {
// Check the location of each desired element
$('.counter').each(function(i) {
var bottom_of_object = $(this).offset().top + $(this).outerHeight();
var bottom_of_window = $(window).scrollTop() + $(window).height();
var counted = $(this).data("counted");
// If the object is completely visible in the window, fade it it
if (!counted && bottom_of_window > bottom_of_object) {
$(this).data("counted", true);
var $this = $(this);
$({
Counter: 0
}).animate({
Counter: $this.attr('data-to')
}, {
duration: 2000,
easing: 'swing',
step: function() {
$this.text(Math.ceil(this.Counter));
},
complete() {
$this.text(Math.ceil(this.Counter));
}
});
}
});
});
/*
SCROLL FUNCTIONS
********************************/
// Every time the window is scrolled...
$(window).scroll(function () {
// Check the location of each desired element
$('.count').each(function (i) {
var bottom_of_object = $(this).offset().top + $(this).outerHeight();
var bottom_of_window = $(window).scrollTop() + $(window).height();
// If the object is completely visible in the window, fade it it
if (bottom_of_window > bottom_of_object) {
var $this = $(this);
$({
Counter: 0
}).animate({
Counter: $this.attr('data-to')
}, {
duration: 2000,
easing: 'swing',
step: function () {
$this.text(Math.ceil(this.Counter));
},
complete(){
$this.text(Math.ceil(this.Counter));
}
});
$(this).removeClass('count').addClass('counted');
}
});
});
<div class="row">
<div class="col">
<div class="row counters text-dark">
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="count" data-to="30000">0</div>
<label>Happy Clients</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="count" data-to="15">0</div>
<label>Years in Business</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-sm-0">
<div class="count" data-to="352">0</div>
<label>Cups of Coffee</label>
</div>
<div class="col-sm-6 col-lg-3">
<div class="count" data-to="178">0</div>
<label>High Score</label>
</div>
</div>
</div>
</div>
Listening to scroll event is not performance friendly, you should really consider using Intersection Observer for stuff like this.
First you have to create a new observer:
var options = {
rootMargin: '0px',
threshold: 1.0
}
var observer = new IntersectionObserver(callback, options);
Here we define that once your target Element is 100% visible in the viewport (threshold of 1) your callback Function is getting executed. Here you can define another percentage, 0.5 would mean that the function would be executed once your element is 50% visible.
Then you have to define which elements to watch, in your case this would be the counter elements:
var target = document.querySelector('.counter');
observer.observe(target);
Last you need to specify what should happen once the element is visible in your viewport by defining the callback function:
var callback = function(entries, observer) {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// here you animate the counter
});
};
In your specific case you probably won't run into performance problems but if you have more and more elements you will start to notice something. So it's better to know of this and to "do it right" if you come across this problem again.
If you need to support older browsers, use the official polyfill from w3c.
You can also remove the observer from any element if you don't need element where

How to shorten this code?

I'm trying to create on() mouseenter function for each element, but is there any way to shorten that function somehow. The problem is that I've created that on mouseenter function several times. Please help guys :)
This is the code below
var $member1 = $('.team-content img:nth-child(1)'),
$member2 = $('.team-content img:nth-child(2)'),
$member3 = $('.team-content img:nth-child(3)'),
$member4 = $('.team-content img:nth-child(4)')
$(".member1").on('mouseenter', function() {
$member1.css({
"left": "0px"
});
}).on('mouseleave', function() {
$member1.css({
"left": ""
});
});
$(".member2").on('mouseenter', function() {
$member2.css({
"left": "0px"
});
}).on('mouseleave', function() {
$member2.css({
"left": ""
});
});
$(".member3").on('mouseenter', function() {
$member3.css({
"left": "0px"
});
}).on('mouseleave', function() {
$member3.css({
"left": ""
});
});
$(".member4").on('mouseenter', function() {
$member4.css({
"left": "0px"
});
}).on('mouseleave', function() {
$member4.css({
"left": ""
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="team" class="about-team">
<div class="team-header">
<h2 class="team-text">Our Team</h2>
<div class="divider"></div>
</div>
<div class="section-content">
<div class="row text-center">
<div class="col-xs-6 col-md-3 col-lg-3 member1">
<h2 class="t-seperator">John Doe</h2>
<span>/CEO</span>
</div>
<div class="col-xs-6 col-md-3 col-lg-3 member2">
<h2 class="t-seperator">Jesica Ice</h2>
<span>/DESIGNER</span>
</div>
<div class="col-xs-6 col-md-3 col-lg-3 member4">
<h2 class="t-seperator">Anna Moon</h2>
<span>/MARKETER</span>
</div>
<div class="col-xs-6 col-md-3 col-lg-3 member3">
<h2 class="t-seperator">Michael Huge</h2>
<span>/DEVELOPER</span>
</div>
</div>
</div>
</div>
<div id="main-team" class="team-content">
<img src="assets/img/team/team1.jpeg" alt="Team 1">
<img src="assets/img/team/team2.jpg" alt="Team 2">
<img src="assets/img/team/team3.jpg" alt="Team 3">
<img src="assets/img/team/team4.jpg" alt="Team 4">
</div>
You can add a general handler and apply the function on the corresponding .member* element if you detect the index of the hovered img using the index() function, Here is an example: (In the example I change the color just for clarity)
$(".team-content img").on('mouseenter', function(e) {
var imageIndex = $(".team-content img").index(e.target) + 1;
$(".member" + imageIndex).css({
"color": "red"
});
}).on('mouseleave', function(e) {
var imageIndex = $(".team-content img").index(e.target) + 1;
$(".member" + imageIndex).css({
"color": "black"
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="team" class="about-team">
<div class="team-header">
<h2 class="team-text">Our Team</h2>
<div class="divider"></div>
</div>
<div class="section-content">
<div class="row text-center">
<div class="col-xs-6 col-md-3 col-lg-3 member1">
<h2 class="t-seperator">John Doe</h2>
<span>/CEO</span>
</div>
<div class="col-xs-6 col-md-3 col-lg-3 member2">
<h2 class="t-seperator">Jesica Ice</h2>
<span>/DESIGNER</span>
</div>
<div class="col-xs-6 col-md-3 col-lg-3 member4">
<h2 class="t-seperator">Anna Moon</h2>
<span>/MARKETER</span>
</div>
<div class="col-xs-6 col-md-3 col-lg-3 member3">
<h2 class="t-seperator">Michael Huge</h2>
<span>/DEVELOPER</span>
</div>
</div>
</div>
</div>
<div id="main-team" class="team-content">
<img src="assets/img/team/team1.jpeg" alt="Team 1">
<img src="assets/img/team/team2.jpg" alt="Team 2">
<img src="assets/img/team/team3.jpg" alt="Team 3">
<img src="assets/img/team/team4.jpg" alt="Team 4">
</div>
You can just use a loop to get them all done at once!
for (var i = 1; i <= 4; i++) {
$('.member' + i).on('mouseenter', function() {
$('.team-content img:nth-child(' + i + ')').css({'left': '0px'});
}).on('mouseleave', function() {
$('.team-content img:nth-child(' + i + ')').css({'left': ''});
});
}
I may have over-thought it, but if the member number and the nth-child number are the same, why not use that to create the relevant accessor? I've got three functions here: the first runs on initialization and saves the integer portion of the member number as a data attribute for later retrieval. The mouseenter and mouseleave functions retrieve that saved member number, and build the selector using that.
$("div[class*='member']").each(function() {
// for every member element, let's save its
// relevant nth-child number.
var myNumber = 0;
var myClasses = $(this).prop("class").split(" ");
// check all classes to find the member number
for (var i = 0; i <= myClasses.length; i++) {
if ( myClasses[i].startsWith("member") ) {
// strip out JUST the number portion.
myNumber = myClasses[i].match(/\d+/)[0];
}
if( myNumber != 0 )
break;
}
// Save the number portion for later.
$(this).data("nthNumber", myNumber);
}).on('mouseenter', function() {
// retrieve the saved number
var selector = ".team-content img:nth-child("+ $(this).data("nthNumber") +" )";
$(selector).show();
}).on('mouseleave', function() {
// retrieve the saved number
var selector = ".team-content img:nth-child("+ $(this).data("nthNumber") +" )";
$(selector).hide();
});
.about-team {
width: 400px;
float: left;
}
.team-content {
position: absolute;
right: 5px;
top: 5px;
}
.team-content img {
display: none;
border: 1px dotted red;
width: 100px;
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="team" class="about-team">
<div class="team-header">
<h2 class="team-text">Our Team</h2>
<div class="divider"></div>
</div>
<div class="section-content">
<div class="row text-center">
<div class="col-xs-6 col-md-3 col-lg-3 member1">
<h2 class="t-seperator">John Doe</h2>
<span>/CEO</span>
</div>
<div class="col-xs-6 col-md-3 col-lg-3 member2">
<h2 class="t-seperator">Jesica Ice</h2>
<span>/DESIGNER</span>
</div>
<div class="col-xs-6 col-md-3 col-lg-3 member4">
<h2 class="t-seperator">Anna Moon</h2>
<span>/MARKETER</span>
</div>
<div class="col-xs-6 col-md-3 col-lg-3 member3">
<h2 class="t-seperator">Michael Huge</h2>
<span>/DEVELOPER</span>
</div>
</div>
</div>
</div>
<div id="main-team" class="team-content">
<img src="assets/img/team/team1.jpeg" alt="Team 1">
<img src="assets/img/team/team2.jpg" alt="Team 2">
<img src="assets/img/team/team3.jpg" alt="Team 3">
<img src="assets/img/team/team4.jpg" alt="Team 4">
</div>
So a few changes made: first, I'd left the initial selector wrong. Then, I added some CSS styles so we could see something happening. The advantage of this approach (while it may be longer) is that it's extensible. If you add thirty more employees, you'd have to create a variable for each one, and use that each time. By this approach, it's happening automatically.

AngularJS - passing a value to a function for the second time returns undefined

I'm new to Angular and on my way to learn while bulding a website I've stopped with a really stupid moment.
I have a function inside a controller that is supposed to be called on ng-click event, I'm passing an 'id' value to it and using that 'id' it's supposed to search an array of presenters(objects) returning and assigning to $scope.presenter the one that I'm looking for. The thing is that the functions works ok for the the first time, but when I'm trying to call it again using a next/previous button the console log returns that the 'id' is undefined. Here is the controller code:
angular.module('fpl15App').controller('PresentersCtrl', function ($scope, $filter) {
$('body').css({'overflow':'hidden'});
$scope.showDetails = false;
$scope.currentPresenter = {};
$scope.getPresenterDetails = function( presenterId ) {
var id = presenterId - 1;
console.log(id);
$scope.showDetails = true;
var i=0, len=$scope.presenters.length;
for (; i<len; i++) {
if (+$scope.presenters[i].id === +id) {
return $scope.currentPresenter = $scope.presenters[i];
}
}
return null;
};
$scope.hideOverlay = function(){
$scope.showDetails = false;
};
$scope.presenters = [
{
id: 1,
name: 'adam_wolf',
thumb: 'images/presenters/adam_wolf.jpg',
bio: 'lorem ipsum'
}.
...
{
id: 15,
name: 'aimee_nicotera',
thumb: 'images/presenters/aimee_nicotera.jpg',
bio: 'lorem ipsum'
}
];
});
end here is the view code:
<div class="row" id="presenters">
<div class="col-md-12 above-element" id="presenter-overlay" ng-show="showDetails">
<div class="col-md-12 col-md-offset-6 motion-container animated" ng-class="{fadeInRight : showDetails}">
<div class="col-md-12 skew-container bg-yellow no-pad">
<div class="skew-content col-md-6 no-pad">
<div class="row presenter-image-container">
<div class="col-md-6 flex-container name-holder">
<h2>{{currentPresenter.name}}</h2>
</div>
<div class="col-md-6 no-pad image-holder">
<figure><img src="{{currentPresenter.image}}" alt=""></figure>
</div>
</div>
<div class="row bg-white presenter-bio-container">
<div class="col-md-6 col-md-offset-6">
<p>{{currentPresenter.bio}}</p>
</div>
</div>
<div class="row bg-white presenter-navigation">
<div class="col-md-10 col-md-offset-2">
<div class="row">
<div class="col-md-6 no-pad"> <span class="presenter-nav" ng-click="getPresenterDetails({{currentPresenter.id - 1}})"> <i class="glyphicon glyphicon-menu-left"></i> Prev </span> </div>
<div class="col-md-6 no-pad"> <span class="presenter-nav" ng-click="getPresenterDetails({{currentPresenter.id + 1}})"> Next <i class="glyphicon glyphicon-menu-right"></i> </span> </div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12 under-element">
<ul id="presenters-list">
<li class="presenter animation" ng-repeat="presenter in presenters"> <a ng-href="" ng-click="getPresenterDetails({{presenter.id}})"><img ng-src="{{presenter.thumb}}" alt="{{presenter.name}}"></a> </li>
</ul>
</div>
</div>

Categories