I'm trying to create an accordion widget in jquery similar to jquery's accordion plugin, with the difference that I want the handles to appear below their respective content instead of above. My accordion works by decreasing the height of the open content section while at the same time increasing the height of the clicked content section. I've posted an example here. My problem is that the animations aren't started at exactly the same time, and there is a noticeable "jump" due to the slight delay before the second animation is started.
Scriptaculous has a function called Effect.Parallel that allows you to create an array of animation effects and execute them in parallel. Unfortunately, I can't seem to find something similar with jquery.
Is there a way I can run precise parallel animations on separate divs in jquery?
Edit: I'm as much interested in alternative methods of coding this accordion widget. So if there is any other method people think would work I'm open to that.
One more answer, hopefully my last one...
Unfortunately, John Resig's syncAnimate method is not quite up to snuff for the accordion-type animation I want to do. While it works great on Firefox, I couldn't get it working smoothly on IE or Safari.
With that said, I decided to bite the bullet and write my own animation engine that does simple parallel animations. The class-code uses jquery functions but is not a jquery plugin. Also, I've only set it up to do size/position animations, which is all I need.
ParallelAnimations = function(animations, opts){
this.init(animations, opts);
};
$.extend(ParallelAnimations.prototype, {
options: {
duration: 250
},
rules: {},
init: function(animations, opts){
// Overwrite the default options
$.extend(this.options, opts);
// Create a set of rules to follow in our animation
for(var i in animations){
this.rules[i] = {
element: animations[i].element,
changes: new Array()
};
for(var style in animations[i].styles){
// Calculate the start and end point values for the given style change
var from = this.parse_style_value(animations[i].element, style, "");
var to = this.parse_style_value(animations[i].element, style, animations[i].styles[style]);
this.rules[i].changes.push({
from: from,
to: to,
style: style
});
}
}
this.start()
},
/*
* Does some parsing of the given and real style values
* Allows for pixel and percentage-based animations
*/
parse_style_value: function(element, style, given_value){
var real_value = element.css(style);
if(given_value.indexOf("px") != -1){
return {
amount: given_value.substring(0, (given_value.length - 2)),
unit: "px"
};
}
if(real_value == "auto"){
return {
amount: 0,
unit: "px"
};
}
if(given_value.indexOf("%") != -1){
var fraction = given_value.substring(0, given_value.length - 1) / 100;
return {
amount: (real_value.substring(0, real_value.length - 2) * fraction),
unit: "px"
};
}
if(!given_value){
return {
amount: real_value.substring(0, real_value.length - 2),
unit: "px"
};
}
},
/*
* Start the animation
*/
start: function(){
var self = this;
var start_time = new Date().getTime();
var freq = (1 / this.options.duration);
var interval = setInterval(function(){
var elapsed_time = new Date().getTime() - start_time;
if(elapsed_time < self.options.duration){
var f = elapsed_time * freq;
for(var i in self.rules){
for(var j in self.rules[i].changes){
self.step(self.rules[i].element, self.rules[i].changes[j], f);
}
}
}
else{
clearInterval(interval);
for(var i in self.rules){
for(var j in self.rules[i].changes)
self.step(self.rules[i].element, self.rules[i].changes[j], 1);
}
}
}, 10);
},
/*
* Perform an animation step
* Only works with position-based animations
*/
step: function(element, change, fraction){
var new_value;
switch(change.style){
case 'height':
case 'width':
case 'top':
case 'bottom':
case 'left':
case 'right':
case 'marginTop':
case 'marginBottom':
case 'marginLeft':
case 'marginRight':
new_value = Math.round(change.from.amount - (fraction * (change.from.amount - change.to.amount))) + change.to.unit;
break;
}
if(new_value)
element.css(change.style, new_value);
}
});
Then the original Accordion class only needs to be modified in the animate method to make use of the new call.
Accordion = function(container_id, options){
this.init(container_id, options);
}
$.extend(Accordion.prototype, {
container_id: '',
options: {},
active_tab: 0,
animating: false,
button_position: 'below',
duration: 250,
height: 100,
handle_class: ".handle",
section_class: ".section",
init: function(container_id, options){
var self = this;
this.container_id = container_id;
this.button_position = this.get_button_position();
// The height of each section, use the height specified in the stylesheet if possible
this.height = $(this.container_id + " " + this.section_class).css("height");
if(options && options.duration) this.duration = options.duration;
if(options && options.active_tab) this.active_tab = options.active_tab;
// Set the first section to have a height and be "open"
// All the rest of the sections should have 0px height
$(this.container_id).children(this.section_class).eq(this.active_tab)
.addClass("open")
.css("height", this.height)
.siblings(this.section_class)
.css("height", "0px");
// figure out the state of the handles
this.do_handle_logic($(this.container_id).children(this.handle_class).eq(this.active_tab));
// Set up an event handler to animate each section
$(this.container_id + " " + this.handle_class).mouseover(function(){
if(self.animating)
return;
self.animate($(this));
});
},
/*
* Determines whether handles are above or below their associated section
*/
get_button_position: function(){
return ($(this.container_id).children(":first").hasClass(this.handle_class) ? 'above' : 'below');
},
/*
* Animate the accordion from one node to another
*/
animate: function(handle){
var active_section = (this.button_position == 'below' ? handle.prev() : handle.next());
var open_section = handle.siblings().andSelf().filter(".open");
if(active_section.hasClass("open"))
return;
this.animating = true;
// figure out the state of the handles
this.do_handle_logic(handle);
// Close the open section
var arr = new Array();
arr.push({
element: open_section,
styles: {
"height": "0px"
}
});
arr.push({
element: active_section,
styles: {
"height": this.height
}
});
new ParallelAnimations(arr, {duration: this.duration});
var self = this;
window.setTimeout(function(){
open_section.removeClass("open");
active_section.addClass("open");
self.animating = false;
}, this.duration);
},
/*
* Update the current class or "state" of each handle
*/
do_handle_logic: function(handle){
var all_handles = handle.siblings(".handle").andSelf();
var above_handles = handle.prevAll(this.handle_class);
var below_handles = handle.nextAll(this.handle_class);
// Remove all obsolete handles
all_handles
.removeClass("handle_on_above")
.removeClass("handle_on_below")
.removeClass("handle_off_below")
.removeClass("handle_off_above");
// Apply the "on" state to the current handle
if(this.button_position == 'below'){
handle
.addClass("handle_on_below");
}
else{
handle
.addClass("handle_on_above");
}
// Apply the off above/below state to the rest of the handles
above_handles
.addClass("handle_off_above");
below_handles
.addClass("handle_off_below");
}
});
The HTML is still called the same way:
<html>
<head>
<title>Parallel Accordion Animation</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="ui.js"></script>
<script type="text/javascript">
$(document).ready(function(){
new Accordion("#accordion");
});
</script>
<style type="text/css">
#accordion{
position: relative;
}
#accordion .handle{
width: 260px;
height: 30px;
background-color: orange;
}
#accordion .section{
width: 260px;
height: 445px;
background-color: #a9a9a9;
overflow: hidden;
position: relative;
}
</style>
</head>
<body>
<div id="accordion">
<div class="section"><!-- --></div>
<div class="handle">handle 1</div>
<div class="section"><!-- --></div>
<div class="handle">handle 2</div>
<div class="section"><!-- --></div>
<div class="handle">handle 3</div>
<div class="section"><!-- --></div>
<div class="handle">handle 4</div>
<div class="section"><!-- --></div>
<div class="handle">handle 5</div>
</div>
</body>
</html>
There are a few things I may add in the future:
- Queued Animations
- Animations for other types of styles (colors,etc)
John Resig posted a synchronized animation sample (no instructions, click a colored box). It might take some work to figure out how to apply it to your control, but it could be a good place to start.
This does not solve running animations in parallel however it reproduces your expected behavior without the jitter. I placed section inside of handle to reduce the number of animations. You could use andSelf() to make the code smaller but it would be harder to read. You will need to make some style tweaks.
<html>
<head>
<title>Accordion Test</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#accordion .handle").click(function(){
var open = $(this).parent().children(".section, .open");
var active = $(this);
if (!active.hasClass("open"))
{
if (active.hasClass("up"))
{
console.log("up");
active.animate({top:"+=100"}).removeClass("up");
active.nextAll(".handle").andSelf().filter(".up").animate({top:"+=100"}).removeClass("up");
$(".section", active).slideUp();
$(".section", active.nextAll()).slideUp();
$(".section", active.prev()).slideDown();
}
else
{
active.prevAll(".handle").not(".up").animate({top:"-=100"}).addClass("up");
$(".section", active.prev()).slideDown();
}
open.removeClass("open");
active.addClass("open");
}
});
});
</script>
<style type="text/css">
#accordion{
width: 200px;
position:relative;
}
#accordion .section{
width: 196px;
margin-left: 2px;
height: 100px;
background-color: #b9b9b9;
display:none;
}
#accordion .handle{
width: 200px;
height: 30px;
background-color: #d9d9d9;
border: 1px solid black;
cursor: pointer;
cursor: hand;
position: absolute;
}
#accordion .handle .header {
height: 30px;
}
</style>
</head>
<body>
<div id="accordion">
<div id="s1" class="section open" style="display:block">This is section 1</div>
<div class="handle open" style="top:100;">
<div class="header">handle 1</div>
<div class="section">This is section 2</div>
</div>
<div class="handle" style="top:130;">
<div class="header">handle 2</div>
<div class="section">This is section 3</div>
</div>
<div class="handle" style="top:160;">
<div class="header">handle 3</div>
<div class="section">This is section 4</div>
</div>
<div class="handle" style="top:190;">
<div class="header">handle 4</div>
<div class="section">This is section 5</div>
</div>
<div class="handle" style="top:220;">
<div class="content">handle 5</div>
</div>
</div>
</body>
</html>
Thanks Adam Plumb for a really great solution to parallel animations. I had a small problem with it though and that was that it somehow saved roles from earlier animations i fixed that by setting the rules to {} before adding them in the init function. It can probably be done in a better way though. I also added a callback function that is called when the animation have finished.
ParallelAnimations = function(animations, opts){
this.init(animations, opts);
};
$.extend(ParallelAnimations.prototype, {
options: {
duration: 250,
callback: null
},
rules: {},
init: function(animations, opts){
// Overwrite the default options
$.extend(this.options, opts);
// Create a set of rules to follow in our animation
this.rules = {}; // Empty the rules.
for(var i in animations){
this.rules[i] = {
element: animations[i].element,
changes: new Array()
};
for(var style in animations[i].styles){
// Calculate the start and end point values for the given style change
var from = this.parse_style_value(animations[i].element, style, "");
var to = this.parse_style_value(animations[i].element, style, animations[i].styles[style]);
this.rules[i].changes.push({
from: from,
to: to,
style: style
});
}
}
this.start()
},
/*
* Does some parsing of the given and real style values
* Allows for pixel and percentage-based animations
*/
parse_style_value: function(element, style, given_value){
var real_value = element.css(style);
if(given_value.indexOf("px") != -1){
return {
amount: given_value.substring(0, (given_value.length - 2)),
unit: "px"
};
}
if(real_value == "auto"){
return {
amount: 0,
unit: "px"
};
}
if(given_value.indexOf("%") != -1){
var fraction = given_value.substring(0, given_value.length - 1) / 100;
return {
amount: (real_value.substring(0, real_value.length - 2) * fraction),
unit: "px"
};
}
if(!given_value){
return {
amount: real_value.substring(0, real_value.length - 2),
unit: "px"
};
}
},
/*
* Start the animation
*/
start: function(){
var self = this;
var start_time = new Date().getTime();
var freq = (1 / this.options.duration);
var interval = setInterval(function(){
var elapsed_time = new Date().getTime() - start_time;
if(elapsed_time < self.options.duration){
var f = elapsed_time * freq;
for(var i in self.rules){
for(var j in self.rules[i].changes){
self.step(self.rules[i].element, self.rules[i].changes[j], f);
}
}
}
else{
clearInterval(interval);
for(var i in self.rules){
for(var j in self.rules[i].changes)
self.step(self.rules[i].element, self.rules[i].changes[j], 1);
}
if(self.options.callback != null) {
self.options.callback(); // Do Callback
}
}
}, 10);
},
/*
* Perform an animation step
* Only works with position-based animations
*/
step: function(element, change, fraction){
var new_value;
switch(change.style){
case 'height':
case 'width':
case 'top':
case 'bottom':
case 'left':
case 'right':
case 'marginTop':
case 'marginBottom':
case 'marginLeft':
case 'marginRight':
new_value = Math.round(change.from.amount - (fraction * (change.from.amount - change.to.amount))) + change.to.unit;
break;
}
if(new_value)
element.css(change.style, new_value);
}
});
I think your problem isn't timing but fractional division of a pixel. If you try this code it looks smooth for handle 1 and 2 but not others in Firefox 3 but still looks jumpy in chrome.
active
.animate({ height: "100px" })
.siblings(".section")
.animate({ height: "0px" });
Have you thought about making the position of the elements static or absolute? If your only moving the position of two elements you don't have to worry about the other ones jumping. Give me a second and I'll try to make an example.
Update: I'm no longer using John Resig's syncAnimate plugin. See my later answer for the final solution
I just wanted to supply the final working solution that I'm employing on my project. It uses the syncAnimate plugin that John Resig wrote (posted by Corbin March).
This code will:
Read and use the section height from CSS
Allow you to set the animation duration, and default active section through an options object.
Automatically detect handle position relative to section and adjusts accordingly. So you move the handles above or below a section in the markup and not have to change the js code.
HTML
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="ui.js"></script>
<script type="text/javascript">
$(document).ready(function(){
new Accordion("#accordion", {active_tab: 0});
});
</script>
<style type="text/css">
#accordion .handle{
width: 260px;
height: 30px;
background-color: orange;
}
#accordion .section{
width: 260px;
height: 445px;
background-color: #a9a9a9;
overflow: hidden;
position: relative;
}
</style>
<div id="accordion">
<div class="section">Section Code</div>
<div class="handle">handle 1</div>
<div class="section">Section Code</div>
<div class="handle">handle 2</div>
<div class="section">Section Code</div>
<div class="handle">handle 3</div>
<div class="section">Section Code</div>
<div class="handle">handle 4</div>
<div class="section">Section Code</div>
<div class="handle">handle 5</div>
</div>
ui.js
Accordion = function(container_id, options){
this.init(container_id, options);
}
$.extend(Accordion.prototype, {
container_id: '',
options: {},
active_tab: 0,
animating: false,
button_position: 'below',
duration: 250,
height: 100,
handle_class: ".handle",
section_class: ".section",
init: function(container_id, options){
var self = this;
this.container_id = container_id;
this.button_position = this.get_button_position();
// The height of each section, use the height specified in the stylesheet if possible
this.height = $(this.container_id + " " + this.section_class).css("height");
if(options && options.duration) this.duration = options.duration;
if(options && options.active_tab) this.active_tab = options.active_tab;
// Set the first section to have a height and be "open"
// All the rest of the sections should have 0px height
$(this.container_id).children(this.section_class).eq(this.active_tab)
.addClass("open")
.css("height", this.height)
.siblings(this.section_class)
.css("height", "0px");
// figure out the state of the handles
this.do_handle_logic($(this.container_id).children(this.handle_class).eq(this.active_tab));
// Set up an event handler to animate each section
$(this.container_id + " " + this.handle_class).mouseover(function(){
if(self.animating)
return;
self.animate($(this));
});
},
/*
* Determines whether handles are above or below their associated section
*/
get_button_position: function(){
return ($(this.container_id).children(":first").hasClass(this.handle_class) ? 'above' : 'below');
},
/*
* Animate the accordion from one node to another
*/
animate: function(handle){
var active_section = (this.button_position == 'below' ? handle.prev() : handle.next());
var open_section = handle.siblings().andSelf().filter(".open");
if(active_section.hasClass("open"))
return;
this.animating = true;
// figure out the state of the handles
this.do_handle_logic(handle);
// Close the open section
open_section
.syncAnimate(active_section, {"height": "0px"}, {queue:false, duration:this.duration}, '')
.removeClass("open");
// Open the new section
active_section
.syncAnimate(open_section, {"height": this.height}, {queue:false, duration:this.duration}, '')
.addClass("open");
var self = this;
window.setTimeout(function(){
self.animating = false;
}, this.duration);
},
/*
* Update the current class or "state" of each handle
*/
do_handle_logic: function(handle){
var all_handles = handle.siblings(".handle").andSelf();
var above_handles = handle.prevAll(this.handle_class);
var below_handles = handle.nextAll(this.handle_class);
// Remove all obsolete handles
all_handles
.removeClass("handle_on_above")
.removeClass("handle_on_below")
.removeClass("handle_off_below")
.removeClass("handle_off_above");
// Apply the "on" state to the current handle
if(this.button_position == 'below'){
handle
.addClass("handle_on_below");
}
else{
handle
.addClass("handle_on_above");
}
// Apply the off above/below state to the rest of the handles
above_handles
.addClass("handle_off_above");
below_handles
.addClass("handle_off_below");
}
});
You can't do a parallel effect in jquery with proper queue and scope. Scriptaculous got it right with queue and scope where jQuery on the other hand has .queue and .animate that are basically useless combined. The only thing jQuery is good for out of the box is pushing some style attributes around on the dom whereas Scriptaculous covers the whole spectrum of what's possible with effects.
You need to use Scriptaculous and John Resig should rethink jQuery.fx, he should have a look at scripty2.com while he's at it.
Related
this is Flickity
I cannot express in English clearly, so if possible please help editing my question.
This is my web page
https://codepen.io/haozun/pen/ywNjvQ
When I open this page, I cannot use arrowkey to navigate different carousel-cell.
I can only use arrowkey after I have mouse clicking on that page.
How can I edit the code so that I can use arrowkey at the very beginning without click on it? i.e. I open this page in my browser, and then I can use arrowkey(37,39) to rotate it immediately. I want to use the keyboardHandlers once I open the page
Flickity.keyboardHandlers = {
// left arrow
37: function() {
var leftMethod = this.options.rightToLeft ? 'next' : 'previous';
this.uiChange();
this[ leftMethod ]();
},
// right arrow
39: function() {
var rightMethod = this.options.rightToLeft ? 'previous' : 'next';
this.uiChange();
this[ rightMethod ]();
},
};
I can edit all source code, including flickity.pkgd.min.js. And my page only contains one flickity.
The official site tells we must use tab (or leftclick) first, then we can really use arrowkeys to change cells.
It almost impossible someone-else will answer this question, And the former answerer has rejected my edit, so I have to answer myself.
Adding this to JS
document.addEventListener("DOMContentLoaded", function() {
init();
});
function init() {
let flkty = new Flickity(".carousel");
flkty.focus()
}
You need to create a new Flickity instance when your app initializes, then call next() on it. You can see in the demo below that as soon as you load your app, it will scroll to the next item in the carousel:
let flkty;
const delayMs = 1000;
document.addEventListener("DOMContentLoaded", function() {
init(true);
});
function init(shouldAutoScroll) {
flkty = new Flickity(".carousel");
if (shouldAutoScroll) {
//Start an carousel auto scroll
autoScroll();
} else {
//Just flip to next item and that's it..
flkty.next();
}
}
function autoScroll() {
if (flkty) {
setInterval(function() {
flkty.next();
}, delayMs);
}
}
.carousel-cell {
min-height: 30em;
padding: 1em;
margin: 0 3em 0 3em;
width: calc( 100vw - 9em);
max-width: 40em;
box-shadow: 0px 16px 20px 5px #7777;
}
<head>
<link rel="stylesheet" href="https://unpkg.com/flickity#2/dist/flickity.min.css">
</head>
<body>
<div class="carousel" id="main" data-flickity='{
"cellAlign": "center",
"contain": true,
"adaptiveHeight":true,
"freeScroll": false,
"accessibility":true,
"wrapAround": true,
"setGallerySize":true,
"hash":true,
"selectedAttraction": 0.05,
"friction": 0.3,
"prevNextButtons":false
}'>
<div class="carousel-cell" id="carousel-cell1">1LONGLONGLONGLONG</div>
<div class="carousel-cell" id="carousel-cell2">2LONGLONGLONGLONG</div>
<div class="carousel-cell" id="carousel-cell3">3LONGLONGLONGLONG</div>
</div>
<script src="https://unpkg.com/flickity#2/dist/flickity.pkgd.min.js"></script>
</body>
My situation is this:
I have a large container the size of the screen which is static and has overflow:hidden. Inside is a very large div that is jqueryui-draggable. Inside that are many many small divs.
The small divs are all hidden by default, I'd like them to appear when they move into the viewport(top parent container) and disappear when moved out. Keep in mind all the moving is done by dragging the very large middle div.
Most of the solutions I've found only work on page scroll. Is there some sort of event I could bind to the draggable?
Disclaimer
I haven't tested this, but hopefully it at least gives you a direction.
The biggest concept to grasp is to check each child to determine whether it is fully within the viewport every time the .drag() method is called on your draggable container. You can modify the logic to fade your elements in / out as needed or to allow the child to be considered visible even before it is fully within view.
CSS
.parent {
position: absolute;
overflow: hidden;
height: 5000px; /* example */
width: 5000px; /* example */
}
.child {
position: absolute;
height: 50px;
width: 50px;
}
HTML
<body>
<div class='parent'>
<div class='child'></div>
<div class='child'></div>
<div class='child'></div>
<!-- ... -->
</div>
</body>
JQUERY
$( ".parent" ).draggable({
drag: function( event, ui ) {
var parentTop = ui.position.top;
var parentLeft = ui.position.left;
var windowHeight = $(window).height();
var windowWidth = $(window).width();
$('.child').each(function(index) {
var childTop = $(this).position().top;
var childLeft = $(this).position().left;
var childWidth = $(this).width();
var childHeight = $(this).height();
// check whether the object is fully within the viewport
// if so - show, if not - hide (you can wire up fade)
((childLeft >= -(parentLeft) && childTop <= -(parentTop) &&
(childLeft + childWidth) <= (-(parentLeft) + windowWidth) &&
(childTop + childHeight) <= (-(parentTop) + windowHeight)))
? $(this).show()
: $(this).hide();
});
}
});
I've been trying to find a solution to my issue for the past few days, but really couldn't find it anywhere, and Google literally hates me, so here I am. This is a big request and my conscience is eating at me for asking, but I don't know where else to turn.
I am building a gallery for a photographer, and while I'm at ease with HTML and CSS, my jQuery skills are taking a beating (in short, they're not good) - surprise, surprise.
The task becomes even more complex since it's a 100% height kind of gallery, and 100% heights don't play nice. I manage to set some of it up, but its functionality is really impaired.
After digging here on Stack and Google I found this great Plugin/Fiddle by William Moynihan:
http://jsfiddle.net/wdm954/8GKz6/11/
It contains exactly my markup and CSS, as well as the functionality I was looking for: a slider which centers the main image when sliding, and is infinite.
However, it doesn't play well with height: 100%; because of the width: auto; property on the images. The following line:
$(content).width(w * $(section).length);
Doesn't appear to calculate the width of the container at all (sets it to zero) because of the auto property in the CSS. When I set the width via the jQuery .css property to ('width', 'auto'), it works fine, but the sliding function is imperfect, causing images to skip/jump when moving right and left.
I didn't resort to a slider, because this is a nice way to actually have the content layed out, in a horizontal manner, which is something that looks great on a photographer's website. And of having width: 100%; will make the vertical images stretch past the browser window, and the horizontal ones to "hang" at the top with plenty of white space below. So, I am convinced that width: auto; and height: 100% is the correct, responsive way to go about it, but if someone manages to "unconvince" me, I will definitely follow your lead.
While I'm here, maybe someone could be polite enough to point me in the right direction to make this gallery finite - ending at the start and end of the slider, with the left/right buttons disappearing accordingly.
Any help is greatly appreciated. Here is the code itself, just in case the fiddle isn't enough:
<div class="container">
<div class="gallery">
<img src="../img/1.jpg" alt="Image" />
<img src="../img/2.jpg" alt="Image" />
<img src="../img/3.jpg" alt="Image" />
<img src="../img/4.jpg" alt="Image" />
<img src="../img/5.jpg" alt="Image" />
</div>
</div>
<nav id="navigation">
<<
>>
</nav>
<script>
/* jQuery Ghost Carousel
* Copyright (c) 2011 William Moynihan
* http://ghosttype.com
* Licensed under MIT
* http://www.opensource.org/licenses/mit-license.php
* May 31, 2011 -- v1.0
*/
$(function() {
var content = '.container .gallery';
var section = content + ' > img';
function ghostCarousel() {
var v = $(window).width();
var w = $(section).width();
var c = (w * $(section).length - v) / 2;
$(content).width(w * $(section).length);
$(content).css('margin-left', -c);
$(content).css('width','auto');
$('#navigation a.left').click(function(e) {
e.preventDefault();
if ($(content).is(':animated')) return false;
$(content).animate({ marginLeft: '-=' +w }, 1000, function() {
var first = $(section).eq(0);
$(section).eq(0).remove();
$(this).animate({ marginLeft: '+=' +w }, 0);
$(this).append(first);
});
});
$('#navigation a.right').click(function(e) {
e.preventDefault();
if ($(content).is(':animated')) return false;
$(content).animate({ marginLeft: '+=' +w }, 1000, function() {
var end = $(section).length - 1;
var last = $(section).eq(end);
$(section).eq(end).remove();
$(this).animate({ marginLeft: '-=' +w }, 0);
$(this).prepend(last);
});
});
}
ghostCarousel();
$(window).resize(function() {
var v = $(window).width();
var w = $(section).width();
var c = (w * $(section).length - v) / 2;
$(content).css('margin-left', -c);
});
});
/* end "jQuery Ghost Carousel" */
</script>
Along with the CSS:
html, body { padding: 0px; }
.container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.container .gallery > img {
position: relative;
float: left;
height: 100%;
}
To make it finite, you need to understand and modify this tow functions only,
$('#gcNav a.left').click(function(e) {
e.preventDefault();
if ($(content).is(':animated')) return false;
$(content).animate({ marginLeft: '-=' +w }, 1000, function() {
var first = $(section).eq(0);//this is first
$(section).eq(0).remove();
$(this).animate({ marginLeft: '+=' +w }, 0);
$(this).append(first);//adding
});
});
$('#gcNav a.right').click(function(e) {
e.preventDefault();
if ($(content).is(':animated')) return false;
$(content).animate({ marginLeft: '+=' +w }, 1000, function() {
var end = $(section).length - 1;
var last = $(section).eq(end);//this is last
$(section).eq(end).remove();
$(this).animate({ marginLeft: '-=' +w }, 0);
$(this).prepend(last);//adding
});
});
Now, in this code, it is working with click on .left, and .right, if you want to make it finite,
just calculate the length of slides, and stop adding the slides, I have added the comments..
I have just pointed out the way...
I hope this will help...
<script language="javascript">
$(document).ready(function($) {
var methods = {
init: function(options) {
this.children(':first').stop();
this.marquee('play');
},
play: function() {
var marquee = this,
pixelsPerSecond = 100,
firstChild = this.children(':first'),
totalHeight = 0,
difference,
duration;
// Find the total height of the children by adding each child's height:
this.children().each(function(index, element) {
totalHeight += $(element).innerHeight();
});
// The distance the divs have to travel to reach -1 * totalHeight:
difference = totalHeight + parseInt(firstChild.css('margin-top'), 10);
// The duration of the animation needed to get the correct speed:
duration = (difference/pixelsPerSecond) * 1000;
// Animate the first child's margin-top to -1 * totalHeight:
firstChild.animate(
{ 'margin-top': -1 * totalHeight },
duration,
'linear',
function() {
// Move the first child back down (below the container):
firstChild.css('margin-top', marquee.innerHeight());
// Restart whole process... :)
marquee.marquee('play');
}
);
},
pause: function() {
this.children(':first').stop();
}
};
$.fn.marquee = function(method) {
// Method calling logic
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.marquee');
}
};
})(jQuery);
var marquee = $('#marquee');
marquee.marquee();
marquee.hover(function() {
marquee.marquee('pause');
}, function() {
marquee.marquee('play');
});
</script>
<style type="text/css">
#marquee {
margin:inherit;
width:auto;
height:inherit
}
</style>
I would like to create a scroller using jquery but I fail. The above code is the marquee I use to scroll up my items. And I am using it as below,
<html>
<body>
<div class="content">
<div id="marquee">
<ul>
<li>...</li>
....
</ul>
</div>
</div></body>
</html>
But it doesn't scroll at all, is there something incorrect in the code I am using you can find for me ?
Not sure if margin-top should work for this at all.
Try using position:relative for holder block(marquee) and position:absolute for content (ul). And update top instead of margin top. But in this case you may need to specify height and overflow:hidden for marquee div. Another options is to set height and oveflow:hidden for marquee, but leave position default. And scroll content using scrollTop or with some similar jquery functions.
I have this javascript file which is a modified version of the VideoLightBox script:
jQuery(function(){
var $=jQuery;
var swfID = "video_overlay";
if(!document.getElementById("vcontainer")){
$("body").append($("<div id='voverlay'></div>"));
$("#voverlay").append($("<div id = 'vcontainer'></div>"));
}
$("#videogallery a[rel]").overlay({
api:true,
expose: (0?{
color:'#424542',
loadSpeed:400,
opacity:0
}:null),
effect:"apple",
onClose: function(){
swfobject.removeSWF(swfID);
},
// create video object for overlay
onBeforeLoad: function(){
// check and create overlay contaner
var c = document.getElementById(swfID);
if(!c){
var d = $("<div></div>");
d.attr({id: swfID});
$("#vcontainer").append(d);
};
var wmkText="© 2011 BORKH";
var wmkLink="http://borkh.co.uk";
c = wmkText? $('<div></div>'):0;
if (c) {
c.css({
position:'absolute',
right:'38px',
top:'38px',
padding:'0 0 0 0'
});
$("#vcontainer").append(c);
};
// for IE use iframe
if (c && document.all){
var f = $('<iframe src="javascript:false"></iframe>');
f.css({
position:'absolute',
left:0,
top:0,
width:'100%',
height:'100%',
filter:'alpha(opacity=0)'
});
f.attr({
scrolling:"no",
framespacing:0,
border:0,
frameBorder:"no"
});
c.append(f);
};
var d = c? $(document.createElement("A")):c;
if(d){
d.css({
position:'relative',
display:'block',
'background-color':'',
color:'#626d73',
'font-family': 'RegisterSansBTNDmRegular, Helvetica, Arial',
'font-size':'11px',
'font-weight':'normal',
'font-style':'normal',
'text-decoration': 'none',
padding:'1px 5px',
opacity:.7,
filter:'alpha(opacity=70)',
width:'auto',
height:'auto',
margin:'0 0 0 0',
outline:'none'
});
d.attr({href:wmkLink});
d.html(wmkText);
d.bind('contextmenu', function(eventObject){
return false;
});
c.append(d);
}
// create SWF
var src = this.getTrigger().attr("href");
if (typeof(d)!='number' && (!c || !c.html || !c.html())) return;
if (false){
var this_overlay = this;
// if local
window.videolb_complite_event = function (){ this_overlay.close() };
// if youtoube
window.onYouTubePlayerReady = function (playerId){
var player = $('#'+swfID).get(0);
if (player.addEventListener) player.addEventListener("onStateChange", "videolb_YTStateChange");
else player.attachEvent("onStateChange", "videolb_YTStateChange");
window.videolb_YTStateChange = function(newState){
if (!newState) this_overlay.close()
}
}
}
swfobject.createSWF(
{ data:src, width:"100%", height:"100%", wmode:"opaque" },
{ allowScriptAccess: "always", allowFullScreen: true, FlashVars: (false?"complete_event=videolb_complite_event()&enablejsapi=1":"") },
swfID
);
}
}); });
The script opens a flash swf file in a "popup" lightbox fashion and plays it either from youtube or via a player locally. I was however wondering if it was possible to create a secondary swf to float on top of the player (noting that this of course would have the wmode:"transparent") and hereby create an opening curtain effect revealing the first swf and the player. I've been trying for quite some time now to load the top clip via createSWF and to create an additional div to contain it and float it using absolute position however I can't seem to get it right.. I know that the div float perfectly on top of each other when using:
<head>
<style type="text/css" media="screen">
<!--
#bottom{
position:absolute;
width: 500px;
height: 400px;
}
#top{
position:absolute;
width:500px;
height:400px;
top: 0px;
left: 0px;
}
-->
</style>
</head>
<body>
<div id="bottom">
"MAIN CLIP"
<div id="top">
"CURTAIN EFFECT"
</div>
</div>
However I'm not strong enough in javascripting to transfer it.
Any help, ideas, hints or suggestions are much appreciated!
Thanks
Andreas
I think your idea will work, but could get quite tricky.
What I would recommend instead is creating a "curtain swf" that instead loads something like the Chromeless YouTube player inside of it. This way you can listen for when the video is done loading/buffering and reveal the curtains when that happens.