JQuery Reload to - javascript

I'm new to JQuery and Javascript, and can't find a good example online of how to refresh just the html object inside a div.
The html object is this graph from cosm.com
index.html
<div id="graph" >
<object type="text/html" style="width:100%; height:100%; margin:1%;"data="https://api.cosm.com/v2/feeds/120687/datastreams/sensor_reading.png?width=740&height=150&colour=%230055ff&duration=5minutes&legend=Vibrations%20measure%20%200%20-%3E%201024&title=Foosball%20Vibrations%20-%205%20Minutes&stroke_size=4&show_axis_labels=true&detailed_grid=true&scale=manual&max=75&timezone=Mountain%20Time%20(US%20%26%20Canada)" >
</object>
</div>
script.js
$(document).ready( function() {
updateGraph;
setInterval (updateGraph, 2000);
}
function updateGraph() {
//This line is not working!!!!
$('#graph').reload();
//$('#graph').load();
}
http://jsfiddle.net/spuder/PNK4k/

Try this out http://jsfiddle.net/Bvpft/16/
Tested in Chrome and FireFox. It refreshes and redownloads the image but not sure what it's supposed to be doing beyond that.
HTML
<!DOCTYPE html>
<body>
<div id="graph" >
<object type="text/html" style="width:100%; height:100%; margin:1%;" data="">
</object>
</div>
</body>
Javascript
$(document).ready( function() {
var graph = $('#graph'),
uri = 'https://api.cosm.com/v2/feeds/120687/datastreams/sensor_reading.png?',
params = {
width: 740,
height: 150,
colour: '#0055ff',
duration: '5minutes',
legend: 'Vibration measure 0 -> 1024 ',
title: 'Foosball Vibrations - 5 Minutes',
show_axis_labels: 'true',
detailed_grid: 'true',
scale: 'manual',
max: 75,
timezone: 'Mountain Time (US & Canada)'
};
var updateGraph = function() {
var data = uri + $.param(params);
graph.empty();
graph.append($('<object/>')
.prop({'type': 'text/html', data: data})
.css({'width': '100%', 'height': '100%'}))
};
updateGraph();
setInterval (updateGraph, 1000);
});
Edited - Changed to Image, made non-flickering, added no-caching (Tested in IE10, Chrome and FireFox): http://jsfiddle.net/JAYDb/4/
HTML
<div id="graph" ></div>
CSS
#graph {
background-color:#C0C0C0;
margin: 0 auto;
min-height: 200px ;
min-width: 750px ;
display: inline-block;
}
#graphObject{
display: block;
width: 100%;
}
Javascript
$(document).ready( function() {
var uri = 'https://api.cosm.com/v2/feeds/120687/datastreams/sensor_reading.png?',
params = {
width: 740,
height: 150,
colour: '#0055ff',
duration: '5minutes',
legend: 'Vibration measure 0 -> 1024 ',
title: 'Foosball Vibrations - 5 Minutes',
show_axis_labels: 'true',
detailed_grid: 'true',
scale: 'manual',
max: 75,
timezone: 'Mountain Time (US & Canada)'
};
var data = uri + $.param(params),
graph = $('#graph'),
obGraph = $('<img/>').prop({'id': 'graphObject'});
var updateGraph = function() {
var date = new Date(),
src = data + '&' + date.getTime();
obGraph.prop('src', '');
obGraph.prop('src', src);
};
graph.empty().append(obGraph);
updateGraph();
setInterval (updateGraph, 1000);
});

A closing bracket is missing?
$(document).ready
( function()
{
updateGraph;
setInterval (updateGraph, 2000);
}
); // missing
You're jsfiddle is not working, because no jQuery version was selected. I've made a working example:
$(document).ready( function() {
alert("OK");
updateGraph();
window.setInterval(updateGraph, 5000);
} );
function updateGraph() {
//This line is not working!!!!
alert("ok");
//$('graph').reload(); // unknown method?
}
http://jsfiddle.net/mmGT9/4/
What do you mean with "$('graph').reload(); ? Is that a custom function?
If you want to reload the "object", I would remove the object from the HTML area and leave just the blank DIV. Then add the following line in your javascript function.
$("#graph").html('<object type="text/html" style="width:100%; height:100%; margin:1%; " data="https://api.cosm.com/v2/feeds/120687/datastreams/sensor_reading.png?width=740&height=150&colour=%230055ff&duration=5minutes&legend=Vibrations%20measure%20%200%20-%3E%201024&title=Foosball%20Vibrations%20-%205%20Minutes&stroke_size=4&show_axis_labels=true&detailed_grid=true&scale=manual&max=75&timezone=Mountain%20Time%20(US%20%26%20Canada)" ></object>');
This will set the object into the element with the id "graph". Existing content within the div will be overridden. The function is called every n seconds and replaces the object, which force a reload. You can do it more pretty by using a variable.
Here is an example: http://jsfiddle.net/mmGT9/5/

jsFiddle
This Works..
I changed your object to an img tag
<img id='obj' style="width:100%; height:100%; margin:1%; " src=...
$('document').ready( function() {
var i;
var src = $('#obj').prop('src');
setInterval(function(){
i++;
$('#obj').prop('src',src+"&"+i);
},5000);
});

Related

fancybox-overlay display two elements

I need help. I tried more or less within a fancybox displaying a photo and next to the picture show with facebook comments social plugin.
within the fancybox options Javascript, I have a
$(".fancybox").fancybox({
padding: 0,
modal: true,
helpers: {
thumbs : {
width : 50,
height : 50
},
buttons: {},
},
afterShow: function(){
var descripcion = "<div class='texto1'><p>TEXTO1</p></div>"
var comentarios = "<div class='texto2'><p>TEXTO2</p></div>;"
$('#fancybox-overlay').append(comentarios);
$('#fancybox-overlay').append(descripcion);
},
nextEffect: 'fade',
prevEffect: 'fade'
});
the problem is it wont let me show the overlay the two things at once, only one or the other ... : S have any solution so you can show both? thank you in advance
If I clearly understand what you are expecting it to do :
afterShow: function(){
var descripcion = '<div class="texto1"><p>TEXTO1</p></div>';
var comentarios = '<div class="texto2"><p>TEXTO2</p></div>';
$('#fancybox-overlay').append(descripcion, comentarios);
}
Watch out for the syntax mistakes ... then you can create a wrapper to insert your new text inside fancybox, let's say
<div class='wrapText'></div>
... with some style to position it correctly
.wrapText {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1100;
color : #fff;
}
... then prepend it to fancybox content using the beforeShow callback
$(".fancybox").fancybox({
beforeShow: function () {
var descripcion = "<div class='texto1'><p>TEXTO1</p></div>";
var comentarios = "<div class='texto2'><p>TEXTO2</p></div>";
$("<div class='wrapText'>" +descripcion+comentarios+ "</div>")
.prependTo( this.inner );
}
});
SEE it working

Creating an swf on an swf with jQuery

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.

nyroModal - how to configure the width and height of the modal box

I am trying to controll the width and height of the modal window, but I have no idea how to override the default settings.
I've been looking at the nmObject (http://nyromodal.nyrodev.com/), but my javascript knowledge is not that great and I have no idea what the correct way to implement this is.
Here is my attempt, but with a syntax error:
$.nmObj( sizes: { initW: 300, initH: 300 });
What am I doing wrong? :\
EDIT:
$(function() {
$('.nyroModal').nyroModal();
$.nmObj({sizes: { initW: 300, initH: 300 }});
});
No syntax errors atleast, but I don't think im using the function correctly
try:
$.nmObj({sizes: { initW: 300, initH: 300 }});
Hope it helps.
Looks like you're using nyroModal v2. For an iframe, you need to set the initial size in the CSS:
.nyroModalCont iframe {
width: 300px;
height: 300px;
}
There's a github issue for this, which is closed, so I think that's how it will work in this version.
Ivailo's answer applies to nyroModal v1, I believe.
Another way of doing it.
<script type="text/javascript">
$(document).ready(function() {
$.nyroModalSettings({
type: 'iframe',
height: 500,
width: 700,
resizable: true,
autoSizable: true,
titleFromIframe: true,
// modal: true,
// selIndicator: '#loading',
contentLoading: "" // use our own
});
$('a.nyroModalPhotos').nyroModal();
});
</script>
For this filter
.* Iframe filter
.* Before: filters.formFile
Locate in min.js the file is part of a string
load: function (nm) { nm.store.iframe = $('<iframe />').attr({ src: 'javascript:\'\';', id: 'nyromodal-iframe-' + (new Date().getTime()), frameborder: '0' })
and add
.css({ height: nm.sizes.h })
the result should be
load: function (nm) { nm.store.iframe = $('<iframe />').attr({ src: 'javascript:\'\';', id: 'nyromodal-iframe-' + (new Date().getTime()), frameborder: '0' }).css({ height: nm.sizes.h })

JQuery resizable problem when dynamically adding elements

ok so i have this script, and what it does is makes an ajax call to a page every time the link is clicked, gets the html element from the ajax puts it into the mainCanvas element, then assign drag and resize features to it.
(example of html recieved from ajax. ###### = the instanceID being passed to it via ajax so the ID is different for each instance)
ajax.php
<div id="Image_######" style=" width:55px; height:55px; position: absolute;">
<img class="Image_######" alt="" title="" src="http://www.imagemagick.org/Usage/thumbnails/hatching_orig.jpg" style="width: 100%; height: 100%;" />
</div>
the problem i am having with this is for some reason only the last element has the abbility to resize, they are all still draggable, but resize is broken for previously added elements as soon as the new element is added.
What i want to do is be able to add multiple elements and resize them all not just the last element added.
i suspect that it may have something to do with how I am appending the response html to the canvas, but I'm not sure. I'm more familiar with standard javascript than i am with the voodoo that is JQuery so any help would be much appreciated.
here is the code below, and here is a link to a running example http://sheac.com/resize-problem/
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.2.0/build/cssreset/reset-min.css">
<link rel="stylesheet" type="text/css" href="media/css/_library/jquery/ui-lightness/jquery-ui-1.8.5.custom.css">
<script type="text/javascript" src="media/js/_library/jquery/jquery-1.4.2.js"></script> <!-- jQuery Base -->
<script type="text/javascript" src="media/js/_library/jquery/jquery-ui-1.8.5.custom.js"></script> <!-- jQuery User-Interface Base -->
<script type="text/javascript" src="media/js/_library/jquery/jquery.ui.resizable.js"></script>
create new instance
<br /><br />
<div id="mainCanvas" style="border: 1px solid #CCC; width: 1000px; height: 500px;"></div>
<script type="text/javascript" language="javascript">
//<![CDATA[
var instanceID = 1000;
var properties = ["draggable", "resizableImage"];
function getInstance(){
var instanceID = getNextInstanceID();
$.get("ajax.php", {instanceID: instanceID},
function(response){
document.getElementById("mainCanvas").innerHTML += response;
runProperties(instanceID);
}
);
}
function getNextInstanceID() {
var iid = instanceID;
instanceID++;
return iid;
}
function runProperties(instanceID) {
if (properties != undefined) {
for (var t in properties) {
assignProperty(properties[t], instanceID);
}
}
}
function assignProperty(property, instanceID) {
switch(property) {
case "draggable":
$("#Image_"+instanceID).addClass(property);
$(function() {
$("." + property).draggable({
grid: [ 16, 16 ],
snap: false,
containment: "#mainCanvas",
scroll: false
});
});
break;
case "resizableImage":
$("#Image_"+instanceID).addClass(property);
$(function() {
$("." + property).resizable({
grid: [ 16, 16 ],
minWidth: 32,
maxWidth: 208,
minHeight: 32,
maxHeight: 208,
aspectRatio: 1/1
});
});
break;
}
}
//]]>
</script>
you make ALL images draggable and resizable in "assignProperty" function.
You just need to do this for new element:
function assignProperty(property, instanceID) {
switch(property) {
case "draggable":
$("#Image_"+instanceID).draggable({
grid: [ 16, 16 ],
snap: false,
containment: "#mainCanvas",
scroll: false
});
break;
case "resizableImage":
$("#Image_"+instanceID).resizable({
grid: [ 16, 16 ],
minWidth: 32,
maxWidth: 208,
minHeight: 32,
maxHeight: 208,
aspectRatio: 1/1
});
break;
}
}

How can I get jquery to execute animations in exact parallel?

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.

Categories