I'm using owl carousel on a menu.
when I scroll to the div the owl carousel auto slide to the right slides.
now when I arrived in the specific div I add class to the slide (active) but for some reason, I can't remove the active class from the other slides (his siblings).
I think it will be best to check the jsfiddle to understand the problem...
<div class="body">
<div class="menu">
<ul class="owl-carousel owl-theme">
Review
a
b
c
d
e
f
</ul>
</div>
JS file
$('.owl-carousel').owlCarousel({
nav: false,
dots: false,
singleItem: true,
})
var owl = $('.owl-carousel');
owl.owlCarousel();
$( window ).scroll(function() {
let scrollbarLocation = $(this).scrollTop();
let scrollLinks = $('.item');
scrollLinks.each(function(){
let sectionOffset = $(this.hash).offset().top;
if (sectionOffset <= scrollbarLocation){
$(this).siblings().removeClass('active-link');
$(this).addClass('active-link');
let goToSlide = $(this).attr('data-num')
owl.trigger('to.owl.carousel', goToSlide);
}
})
if( scrollbarLocation === 0){
scrollLinks.removeClass('active-link');
owl.trigger('to.owl.carousel', 0);
}
});
check https://jsfiddle.net/jt31h4pr/132/
The problem is that you remove/add active-link class on the same element ( this ). You need to removeClass only on the the element that already has class active-link.
The class active is controlled by the plugin and all the elements that are visible have the active class
See below
$('.owl-carousel').owlCarousel({
nav: false,
dots: false,
singleItem: true,
})
var owl = $('.owl-carousel');
owl.owlCarousel();
$( window ).scroll(function() {
let scrollbarLocation = $(this).scrollTop();
let scrollLinks = $('.item');
scrollLinks.each(function(){
let sectionOffset = $(this.hash).offset().top;
if (sectionOffset <= scrollbarLocation){
$('.active-link').removeClass('active-link'); // added
$(this).addClass('active-link');
let goToSlide = $(this).attr('data-num')
owl.trigger('to.owl.carousel', goToSlide);
}
})
if( scrollbarLocation === 0){
scrollLinks.removeClass('active-link');
owl.trigger('to.owl.carousel', 0);
}
});
.body {
height: 5000px;
}
ul {
padding: 0;
margin: 0;
list-style-type: none;
}
.item {
width: 200px;
height: 70px;
background: red;
margin: 0 15px;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
border-bottom: 4px solid transparent;
}
.active-link {
border-bottom: 4px solid #000;
}
.menu {
position: fixed;
top: 0;
}
section {
width: 100%;
height: 600px;
background: #f8f9fb;
}
#a {
background: lightblue;
margin-top: 200px;
}
#b {
background: lightgreen;
}
#c {
background: tomato;
}
#d {
background: lightpink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/owl.carousel.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.carousel.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.theme.default.css">
<div class="body">
<div class="menu">
<ul class="owl-carousel owl-theme">
Review
a
b
c
d
e
f
</ul>
</div>
<section id="a"></section>
<section id="b"></section>
<section id="c"></section>
<section id="d"></section>
</div>
Look into this fiddle
https://jsfiddle.net/09sLpuwd/1/
What are you doing wrong is trying to remove active class from this and then add active class to the same this, which actualy do nothing.
Insted what I propose is to remove active class from all '.item' emelements and than add it to active one.
As alternative aproach you can store previous item and remove class from it, but I think first way is better.
Hi the problem is with below line
$(this).siblings().removeClass('active-link');
replace it with
$("div.active a").removeClass('active-link');
This was removing and adding class on same time on all so what i did is once i am removing the class from all i am adding it on present elemnt
Related
I have a header with a transparent backgrounsd, and I am trying to get the text of the header to change colour between white and black depending on the background of the div it's overlapping.
So far I have managed to add a class of .color-menu to all the divs where I want the header to be black.
I then have it add a class of .dark-menu to the header when the .color-menu div reaches the top of the page.
The problem is that it only works for the first .colour-menu div. It will change to black when it is in the viewport and back to white for the next div but then when the next .color-menu div gets to the top it doesn't change.
So, it seems like the .each function isn't working but I am not sure how to fix it.
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(".color-menu").offset().top;
var bottom_of_element = $(".color-menu").offset().top + $(".color-menu").outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
UPDATE: I have also tried using $(this) but it really throws off when it changes color.
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(this).offset().top;
var bottom_of_element = $(this).offset().top + $(this).outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
Here is a simplified version of my code as an example:
$(document).ready(function() {
$(".white").addClass("color-menu");
$(".white-bold").addClass("color-menu");
$(".light").addClass("color-menu");
$(".light-bold").addClass("color-menu");
$(".bright").addClass("color-menu");
});
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(".color-menu").offset().top;
var bottom_of_element = $(".color-menu").offset().top + $(".color-menu").outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
.header {
width: 100%;
background: rgba(0,0,0,0);
margin: 0;
padding:10px;
position: fixed;
text-align: center;
}
.header a {
color: white;
font-size: 2rem;
text-transform: uppercase;
}
.dark-menu a{
color: black;
}
.black {
background-color: black;
height: 200px;
}
.white, .white-bold, .light, .light-bold, .bright {
background-color: white;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header">
<a>This is the header</a>
</div>
<div class ="black"></div>
<div class ="white"></div>
<div class ="black"></div>
<div class ="white-bold"></div>
<div class ="black"></div>
<div class ="light"></div>
<div class ="black"></div>
<div class ="light-bold"></div>
<div class ="black"></div>
<div class ="bright"></div>
What is happening in your code is that on scroll, you loop through every color-menu div and add the class if it is the current one... but then the code continues to loop though the remaining elements in the array and removes it again because the page is not in the other div.
I've explained step-by-step the changes you need to get this to work after the example, but first you can see it working here:
Working Example:
$(document).ready(function() {
$(".white").addClass("color-menu");
$(".white-bold").addClass("color-menu");
$(".light").addClass("color-menu");
$(".light-bold").addClass("color-menu");
$(".bright").addClass("color-menu");
$(window).scroll(function() {
var inColorMenu = false; /* initialise var to store if we are in color-menu */
var top_of_screen = $(window).scrollTop(); /* just get this once outside loop */
/* Loop through each color-menu element and check if we are in one */
$('.color-menu').each(function(i) {
var top_of_element = $(this).offset().top;
var bottom_of_element = top_of_element + $(this).outerHeight();
/* if we are in a color-menu element, set our var to true and stop processing */
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
inColorMenu = true;
return false; /* N.B. need to return "false" to break from the "each" loop */
}
});
if (inColorMenu) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
.header {
width: 100%;
background: rgba(0, 0, 0, 0);
margin: 0;
padding: 10px;
position: fixed;
text-align: center;
}
.header a {
color: white;
font-size: 2rem;
text-transform: uppercase;
}
.header.dark-menu a {
color: black;
}
.black {
background-color: black;
height: 200px;
}
.white,
.white-bold,
.light,
.light-bold,
.bright {
background-color: white;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header">
<a>This is the header</a>
</div>
<div class="black"></div>
<div class="white"></div>
<div class="black"></div>
<div class="white-bold"></div>
<div class="black"></div>
<div class="light"></div>
<div class="black"></div>
<div class="light-bold"></div>
<div class="black"></div>
<div class="bright"></div>
How this works:
Declare a variable to record whether we are in a "color-menu" class or not, and initialise this to false, e.g.:
var inColorMenu = false;
When looping through $('.color-menu').each, if we are between the top and bottom of one of divs (which your code is already detecting), then set our variable to true to record this.
We can also return false to break the each loop and stop processing the rest of the elements (it will still work without this, we are just reducing the amount of processing required):
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
inColorMenu = true;
return false; /* N.B. need to return "false" to break from the "each" loop */
}
Finally, after we finish our $('.color-menu').each loop, if inColorMenu is true, we know we are in a color-menu div so we add the dark-menu class to the header, otherwise we remove it:
if (inColorMenu) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
Note: You need to use $(this) when getting the offset().top and outerHeight() so that you are getting the values for the current element in the loop. $(".color-menu") gets the values for an unspecified element with this class so will not work.
Have multiple buttons and sections associated with those buttons in the Elementor page builder.
What I am trying to achieve is when page loads, we can only see buttons and section will toggle when someone clicks on the button.
I have managed to achieve that, but when clicking on second button, button 1 section should hide and button 2 section should display.
Here is the w3 schools link for my project
jQuery(document).ready(function( $ ){
var hbtn = $(".coursesBtn");
var hcon = $(".coursesSection");
hcon.hide();
hbtn.click(function(e) {
var index = hbtn.index(this)
$(hcon).eq(index).slideToggle("slow");
e.preventDefault();
});
});
jQuery(document).ready(function( $ ){
var hbtn = $(".videosBtn");
var hcon = $(".videosSection");
hcon.hide();
hbtn.click(function(e) {
var index = hbtn.index(this)
$(hcon).eq(index).slideToggle("slow");
e.preventDefault();
});
});
You can use onclick for buttons for example
<button class="btn1" onclick="f1()">BUTTON1</button>
and add the function to toggle in f1()
Here is a quick example, the idea is to hide the other sections first, and show the section that you want to be displayed;
$(document).ready(function($) {
$('.btn1').click(function() {
$('.section').hide();
$('.section1').show();
})
$('.btn2').click(function() {
$('.section').hide();
$('.section2').show();
})
$('.btn3').click(function() {
$('.section').hide();
$('.section3').show();
})
})
.section {
width: 100%;
padding: 50px 0;
text-align: center;
background-color: lightblue;
margin-top: 20px;
}
.section2 {
background-color: lightgreen;
}
.section3 {
background-color: orange;
}
<button class="btn1">BUTTON1</button>
<button class="btn2">BUTTON2</button>
<button class="btn3">BUTTON3</button>
<div class="section section1">
This is Section1.
</div>
<div class="section section2">
This is Section2.
</div>
<div class="section section3">
This is Section3.
</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
Add a new class for each button (.btn) and section (.section) and use these as in the code below. You can have as many buttons and sections as you want and the code below will work without any modification:
var hbtn = $(".btn");
var hcon = $(".section");
hcon.hide();
hbtn.click(function(e) {
var index = hbtn.index(this);
hcon.hide() //OR hcon.filter(':visible').slideToggle("slow")
.eq(index).slideToggle("slow");
});
DEMO
$(document).ready(function( $ ){
var hbtn = $(".btn");
var hcon = $(".section");
hcon.hide();
hbtn.click(function(e) {
var index = hbtn.index(this);
hcon.hide() //OR hcon.filter(':visible').slideToggle("slow")
.eq(index).slideToggle("slow");
});
});
.section1 {
width: 100%;
padding: 50px 0;
text-align: center;
background-color: lightblue;
margin-top: 20px;
}
.section2 {
width: 100%;
padding: 50px 0;
text-align: center;
background-color: lightblue;
margin-top: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="btn1 btn">BUTTON1</button>
<button class="btn2 btn">BUTTON2</button>
<div class="section1 section">
This is Section1.
</div>
<div class="section2 section">
This is Section2.
</div>
I want my page to show 3 divs at a time, and when I click next I would like it to show the next 3 divs. Then when I click previous, I would like to display the previous 3.
$("#container .result").slice(0, 3).show();
$("#right").click(function () {
var items = $('#container .result:visible').hide().last();
var nextItems = items.nextAll().slice(0, 3);
if (nextItems.length === 0) {
nextItems = $("#container .result").slice(0, 3);
}
nextItems.show();
});
$("#left").click(function () {
var items = $('#container .result:visible').hide().last();
var nextItems = items.prevAll().slice(0, 3);
if (nextItems.length === 0) {
nextItems = $("#container .result").slice(0, 3);
}
nextItems.show();
});
The problem is that when I click previous, and it comes to last 3 divs and when I click again it shows 2 than 1. How can i fix that? I want it to stop when it comes to first 3.
You were very much on the right track, I was impressed by the ingenuity of your code.
Your main problem is solved with a very simple fix; in the #left click-handler, replace .last() with .first():
var items = $('#container .result:visible').hide().first();
And to loop around to the last 3 when you click previous on the first 3, change this line to the next:
nextItems = $("#container .result").slice(0, 3);
nextItems = $("#container .result").slice($("#container .result").length-3, $("#container .result").length);
But I thought the situation might occur, now or in the future, that the number of .results aren't a multitude of 3, let's say 7 or 11 for example.
I created a script that will handle that, and also loop around in both directions:
$("#container .result").first().show(); //initialize divs at pageload
$(".nav").click(function() {
var start=0, step=3;
var currentItems = $("#container .result:visible").hide();
var currentLast = (this.id==="prev" ? currentItems.first() : currentItems.last());
var nextItems = (this.id==="prev" ? currentLast.prevAll() : currentLast.nextAll());
if (nextItems.length === 0) { //if the last set of divs has been reached, loop around
var itemsLength = $("#container .result").length;
if (this.id==="prev") {start=itemsLength-step; step=itemsLength;} //determine wich way to loop around
nextItems = $("#container .result").slice(start,step); //loop around
} else if (nextItems.length < step) { //if the next divs aren't a full set, keep some divs from the current set visible
if (this.id==="prev") {step-=nextItems.length;} else {start=nextItems.length;} //determine which current items should remain visible
currentItems.slice(start,step).each(function(){nextItems.push(this);}); //add selected current items to nextItems-array
} else {nextItems=nextItems.slice(start,step);} //if the next divs are a full set, simply select the next set
nextItems.show(); //show the next set
}).click(); //initialize divs at pageload
In HTML, I gave the two buttons both a class "nav" (see code snippet below), so that I could combine their click-handlers into one.
I changed your first line to this: $("#container .result").first().show();. That line - in combination with the .click() chained to the click-handler - replaces your line: $("#container .result").slice(0, 3).show(); (at the top of your script).
This gives you much more flexibility to change the amount of divs you want to show on the page at once. At the start of the click-handler I declare var step=3;, which is the only place that number is hard-coded, so if you ever want to change the amount you only have to change that number (and maybe adjust some styling).
The rest of the explanation is in the comments in the code.
See the code snippet below for a demo:
$("#container .result").first().show(); //initialize divs at pageload
$(".nav").click(function() {
var start=0, step=3;
var currentItems = $("#container .result:visible").hide();
var currentLast = (this.id==="prev" ? currentItems.first() : currentItems.last());
var nextItems = (this.id==="prev" ? currentLast.prevAll() : currentLast.nextAll());
if (nextItems.length === 0) { //if the last set of divs has been reached, loop around
var itemsLength = $("#container .result").length;
if (this.id==="prev") {start=itemsLength-step; step=itemsLength;} //determine wich way to loop around
nextItems = $("#container .result").slice(start,step); //loop around
} else if (nextItems.length < step) { //if the next divs aren't a full set, keep some divs from the current set visible
if (this.id==="prev") {step-=nextItems.length;} else {start=nextItems.length;} //determine which current items should remain visible
currentItems.slice(start,step).each(function(){nextItems.push(this);}); //add selected current items to nextItems-array
} else {nextItems=nextItems.slice(start,step);} //if the next divs are a full set, simply select the next set
nextItems.show(); //show the next set
}).click(); //initialize divs at pageload
html,body {width:98%; height:90%;}
#container {width:100%; height:90%; background:lightgrey;}
#container .result {display:none; float:left; width:30%; height:100%; margin:0 1.66%; background:lightgreen;}
#container .result > div {display:table; width:100%; height:100%;}
#container .result > div > div {display:table-cell; width:100%; height:100%; text-align:center; vertical-align:middle; font:bolder 2em sans-serif;}
.nav {margin-top:2%; cursor:pointer;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="result"><div><div>1</div></div></div>
<div class="result"><div><div>2</div></div></div>
<div class="result"><div><div>3</div></div></div>
<div class="result"><div><div>4</div></div></div>
<div class="result"><div><div>5</div></div></div>
<div class="result"><div><div>6</div></div></div>
<div class="result"><div><div>7</div></div></div>
</div>
<button type="button" class="nav" id="prev">PREVIOUS</button>
<button type="button" class="nav" id="next">NEXT</button>
codepen: https://codepen.io/anon/pen/YQoJzd?editors=1010
jsfiddle: https://jsfiddle.net/k8ysj6gq/1/
You can ignore the CSS and HTML (except for the class="nav" on the buttons), that's all just so we can see it. All the relevant code is in the JS.
Basically you can do something like below.
On Next or Previous click set margin-left of container to position or loop through all div.
$(document).ready(function() {
$('.next-button').on('click', function() {
if (parseInt($('.carousel-item').css("margin-left").slice(0, -2)) < -2000) {
$('.carousel-item').animate({
"margin-left": "0px"
}, 200)
} else {
$('.carousel-item').animate({
"margin-left": "-=600px"
}, 200);
}
});
$('.prev-button').on('click', function() {
if (parseInt($('.carousel-item').css("margin-left").slice(0, -2)) > 0) {
$('.carousel-item').animate({
"margin-left": "-2000px"
}, 200)
} else {
$('.carousel-item').animate({
"margin-left": "+=600px"
}, 200);
}
});
});
.carousel-container {
height: 500px;
display: flex;
margin: 40px 20px;
position: relative;
overflow: hidden;
width: 720px;
padding: 0;
border: 1px solid red;
align-items: center;
}
.carousel-item {
height: 100%;
margin: 5px;
margin-left: 60px;
padding: 0;
-moz-box-orient: horizontal;
-ms-box-orient: horizontal;
-webkit-box-orient: horizontal;
-o-box-orient: horizontal;
box-orient: horizontal;
display: -moz-box;
display: -ms-box;
display: -webkit-box;
display: -o-box;
display: box;
list-style-type: none;
}
.item {
border: solid 1px #333;
margin-right: 10px;
width: 200px;
display: flex;
align-items: center;
}
.item>a {
width: 100%;
display: flex;
justify-content: center;
align-items: flex-end;
}
.prev-button,
.next-button {
border: 1px solid green;
background-color: gray;
}
.navigation {
width: 60px;
margin: 0;
position: absolute;
top: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
}
.next-button:hover,
.prev-button:hover {
background-color: red;
}
.navigation:active {
color: white;
}
.next-button {
right: 0;
}
.prev-button {
left: 0;
}
/* .carousel-item li:nth-child(1) {
background-image: url('http://urbanphenomena.net/imgs/cover/bq2.jpg');
background-size: cover;
} */
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="carousel-container">
<a class="prev-button navigation" href="#">
<</a>
<div class="carousel-item">
<li class="item"> 1 </li>
<li class="item"> 2 </li>
<li class="item"> 3 </li>
<li class="item"> 4 </li>
<li class="item"> 5 </li>
<li class="item"> 6 </li>
<li class="item"> 7 </li>
<li class="item"> 8 </li>
<li class="item"> 9 </li>
<li class="item">
</li>
</div>
<a class="next-button navigation" href="#">></a>
</div>
Run co
Ok so your first mistake was that when trying to code back 3 you were getting the previous 3 items from the first of the 3 not the last. So i changed .last() to .first(). Then to loop back when previous = 0 all you did was slice from the current 3, instead of slicing at the end of the entire array of elements.
Here's a link to a codepen that has the working code(you'll obviously have to change the variables to fit your project): https://codepen.io/anon/pen/qjzxee?editors=1010
changed var items = $('#container .result:visible').hide().last(); to var items = $('#container .result:visible').hide().first();
and
if (nextItems.length === 0) {
nextItems = $("#container .result").slice(0, 3);
}
to
if (nextItems.length === 0) {
var allItems = $("#container .result");
nextItems = $("li").slice(allItems.length - 3,allItems.length);
}
this also only works if the number elements is a multiple of the number you are skipping each time, but i can fix that if you'd like
How can I make the carousel center the item I've clicked to the middle? I've looked everywhere for an answer but they're not straight answers... Can someone help me in this, please?
This is what I've done so far: http://jsfiddle.net/sp9Jv/
HTML:
<div id="wrapper">
<div id="carousel">
prev
next
<div class="viewport">
<ul>
<li>Un</li>
<li>Deux</li>
<li>Trois</li>
<li>Quatre</li>
<li>Cinq</li>
<li>Six</li>
<li>Sept</li>
<li>Huit</li>
</ul>
</div>
<!-- viewport -->
</div>
<!-- carousel -->
</div>
<!-- wrapper -->
JavaScript:
var carousel = $('#carousel'),
prev = carousel.find('.prev'),
next = carousel.find('.next'),
viewport = carousel.find('.viewport'),
item = viewport.find('li'),
itemWidth = item.outerWidth(true),
itemNum = item.length,
itemList = viewport.find('ul');
itemList.width(itemWidth * itemNum);
var moveCarousel = function(dir) {
itemList.animate({ left: '-=' + (itemWidth * dir) + 'px' }, 400);
};
//prev
prev.on('click', function(e) {
e.preventDefault();
moveCarousel(-1);
});
//next
next.on('click', function(e) {
e.preventDefault();
moveCarousel(1);
});
//carousel item
item.on('click', 'a', function(e) {
var self = $(this),
selfIndex = self.index(),
distance = itemList.width() / 2,
selfPos = self.position(),
selfPosLeft = selfPos.left,
viewportPosLeft = viewport.position().left;
e.preventDefault();
//move item to middle, but it doesn't work...
if (selfPosLeft > Math.floor(viewport.width())/3) {
itemList.animate({ left: '-' + Math.floor(viewport.width())/3 + 'px' }, 400);
}
if (selfPosLeft < Math.floor(viewport.width())/3) {
itemList.animate({ left: Math.floor(viewport.width())/3 + 'px' }, 400);
}
});
CSS:
#wrapper {
width: 500px;
margin: 20px auto;
}
#carousel {
position: relative;
}
.viewport {
width: 260px;
border: 1px solid #6e6e6e;
height: 80px;
overflow: hidden;
position: relative;
margin-left: 100px;
}
.prev, .next {
position: absolute;
}
.prev {
top: 20px;
left: 0;
}
.next {
top: 20px;
right: 0;
}
.viewport ul {
position: absolute;
}
.viewport li {
float: left;
margin-right: 10px;
}
.viewport li a {
display: block;
width: 80px;
height: 80px;
background: #ddd;
}
While you have prepared all the information needed about all items, you can calculate the value of the left based on the clicked item.
Here is my modification:
and I've bound the click action of carousel items with this function and passed the clicked item using the self keyword.
var itemClicked=function(item){
var itemIndex=$(item).index(),
newLeft=(itemIndex*itemWidth*-1)+Math.floor(($(viewport).width()/itemWidth)/2)*itemWidth;
$(itemList).animate({left:newLeft+"px"},400);
};
You can check it working on this url: http://jsfiddle.net/rUZHg/3/
I assume that this should work despite of the number of viewed elements while it calculates the padding between the left 0 and the left of the center element.
Alright, it's ugly, I hope it gives you some ideas.
I created a global currentItem that tracks what's in the center. Every time the carousel moves this is updated.
The very useful variable I found was selfPosLeft which told me what was being clicked. I should add that 90 was the multiple I got from clicking around. Must be linked to your CSS and I don't know how to find this number dynamically.
Please try it :) http://jsfiddle.net/sp9Jv/4/
Well, I'm picturing that when you have more than 3 items you can change the code to compute the difference between the current item and the selfPosLeft of the clicked one, I'll leave that to you :) Like this, seems to work. http://jsfiddle.net/sp9Jv/5/
I am adapting the Coverflow technique to work with a div. Following is the html:
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<style type="text/css" media="screen">
body,html {
margin: 0;
padding: 0;
background: #000;
height: 100%;
color: #eee;
font-family: Arial;
font-size: 10px;
}
div.magnifyme {
height: 80px;
padding: 80px;
position: absolute;
top: 0px;
left: 0px;
width: 2000px;
}
div.wrapper {
margin: 0px;
height: 470px;
/*border: 2px solid #999;*/
overflow: hidden;
padding-left: 40px;
right: 1px;
width: 824px;
position: relative;
}
div.container {position: relative; width: 854px; height: 480px; background: #000; margin: auto;}
div.nav {position: absolute; top: 10px; width: 20%; height: 10%; right: 1px; }
div.magnifyme div {
position: absolute;
width: 300px;
height: 280px;
float: left;
margin: 5px;
position: relative;
border: 2px solid #999;
background: #500;
}
</style>
<script type="text/javascript" src="jquery-1.3.2.js"></script>
<script type="text/javascript" src="ui.coverflow.js"></script>
<script type="text/javascript" src="ui.core.js"></script>
<script type="text/javascript">
$(function() {
$("div.magnifyme").coverflow();
$("#add").click(function() {
$(".magnifyme").append("<div id=\"div5\">hello world</div>");
$("div.magnifyme").coverflow();
});
});
</script>
</head>
<body>
<div class="container">
<div class="wrapper">
<div class="magnifyme">
<div id="div0">This is div 0</div>
<div id="div1">This is div 1</div>
<div id="div2">This is div 2</div>
<div id="div3">This is div 3</div>
<div id="div4">This is div 4</div>
</div>
</div>
<div class="nav">
<button type="button" id="add">Add to Deck</button>
</div>
</div>
</body>
</html>
The coverflow function (included as a js file in the head section) is here. When I click the button, I was expecting it to add a DIV to the already present deck. For some reason, it doesn't show the newly added DIV. I tried calling the coverflow() function after I added the new element but that didn't work either. The modified coverflow function is given here:
;(function($){
$.widget("ui.coverflow", {
init: function() {
var self = this;
this.items = $(this.options.items, this.element).bind("click", function() {
self.moveTo(this);
//$("div.slider").slider("moveTo", self.current, null, true);
});
this.itemWidth = this.items.outerWidth(true);
this.current = 0; //Start item
this.refresh(1, 0, this.current);
this.element.css("left",
(-this.current * this.itemWidth/2)
+ (this.element.parent()[0].offsetWidth/2 - this.itemWidth/2) //Center the items container
- (parseInt(this.element.css("paddingLeft")) || 0) //Subtract the padding of the items container
);
},
moveTo: function(item) {
this.previous = this.current;
this.current = !isNaN(parseInt(item)) ? parseInt(item) : this.items.index(item);
if(this.previous == this.current) return false; //Don't animate when clicking on the same item
var self = this, to = Math.abs(self.previous-self.current) <=1 ? self.previous : self.current+(self.previous < self.current ? -1 : 1);
$.fx.step.coverflow = function(fx) {
self.refresh(fx.now, to, self.current);
};
this.element.stop().animate({
coverflow: 1,
left: (
(-this.current * this.itemWidth/2)
+ (this.element.parent()[0].offsetWidth/2 - this.itemWidth/2) //Center the items container
- (parseInt(this.element.css("paddingLeft")) || 0) //Subtract the padding of the items container
)
}, {
duration: 1000,
easing: "easeOutQuint"
});
/*current = this.current;
$("[id^=div]").each(function() {
if(this.id != "div"+current) {
console.info(this.id + " Current: " + current);
$(this).fadeTo( 'slow', 0.1);
}
});*/
},
refresh: function(state,from,to) {
var self = this, offset = null;
this.items.each(function(i) {
var side = (i == to && from-to < 0 ) || i-to > 0 ? "left" : "right";
var mod = i == to ? (1-state) : ( i == from ? state : 1 );
var before = (i > from && i != to);
$(this).css({
webkitTransform: "matrix(1,"+(mod * (side == "right" ? -0.5 : 0.5))+",0,1,0,0) scale("+(1+((1-mod)*0.5))+")",
left: (
(-i * (self.itemWidth/2))
+ (side == "right"? -self.itemWidth/2 : self.itemWidth/2) * mod //For the space in the middle
),
zIndex: self.items.length + (side == "left" ? to-i : i-to)
});
if(!$.browser.msie)
$(this).css("opacity", 1 - Math.abs((side == "left" ? to-i : i-to))/2);
});
}
});
$.extend($.ui.coverflow, {
defaults: {
items: "> *"
}
});
})(jQuery);
One thing I did notice is that after clicking the button for about 5-10 times, the elements show up but not along with the already present divs but rather below them. I am guessing that this has something to do with the CSS of the magnifyme class (2000px), but I am not sure what it is. Is there any way I can make this work?
You need to write an additional function for the coverflow widget:
add: function(el) {
var self = this;
this.element.append(el)
this.options.items = $('> *', this.element);
this.items = $(this.options.items, this.element).bind("click", function() {
self.moveTo(this);
});
this.itemWidth = this.items.outerWidth(true);
this.moveTo(this.items.length-1);
},
and then call it like so:
$("#add").click(function() {
$("div.magnifyme").coverflow('add', "<div></div>");
});
First, you need to add a references to the jQuery UI core, and it also appears that it requires the jQuery slider plugin.
Second, in your click event you're doing a location.reload, which is refreshing the page from the server, resetting any changes you had made to the page. (if you make the DIVs much smaller you can see one flash in before the page is reloaded).
You are getting a js error on the page -- "$.widget is not a function" because you didn't include the jqueryUI library. http://jqueryui.com/
Also if you remove the location.reload line, your code will work, however, I would rewrite that script block like this, so that everything clearly runs when the document is ready:
<script type="text/javascript">
$(document).ready(function() {
$("div.magnifyme").coverflow();
$("#add").click(function() {
$(".magnifyme").append("<div id=\"div5\">hello world</div>");
$("div.magnifyme").coverflow();
});
});
</script>