I have this design for the front page of a personal website I'm making:
You click on the respective icon and it expands to reveal info.I went through two other designs that didn't work out, but incorporated similar animations. After modifying the JS after each iteration I realized that I was:
Encountering bits of redundant code
Starting to consider the potentiality of adding more tabs/sections in the future (that I might need to make the code scale)
This lead me to start 'generacizing' my code. So making functions like 'handleHorizontalTabs' or 'handleVerticalTabs' rather than 'handleGamesTab' or 'handlePhotographyTab' This would avoid me writing a whole new function for the photography tab, which differs from the games tab by only one css property. So I was thinking, well then I'm going to need to pass in an object rather than an exact ID. Then I created some object literals that stored all of their respective properties. The problem with creating neutral animation functions like this (in my case at least) is that I HAVE to define every property that could be used in the function even if it's not used. Consider this snippet before you scroll to the full code:
if (active){
// Only animate horizontally
$(elemToAnimate.ID).animate({
width: elemToAnimate.ExpandedWidth,
left: elemToAnimate.ExpandedLeft,
right: elemToAnimate.ExpandedRight
}, animateDuration)
}
I might not need to change the say,'left' property, when animating. But because this is a general function it doesn't know that. So, in my object literal I still have to define it (it would just be the original value). I did notice (and I left the game section unchanged so you could see this) that if I simply leave it out of the literal, it is marked as undefined and has no effect on the animation. I could do this, but I don't think that's good practice. Plus anyone reading my code would be asking where that property is.
My question is two fold:
What should I consider programmatically when taking the scalability of an app into account?
How can this code be cleaned up/refined?
The code:
https://jsfiddle.net/rc6wnsst/
(PS Not browser optimized; use Mozilla if you can)
$(document).ready(function() {
// Definitions
//Define object literals
var aboutmeSection = {id: '#aboutme-section', get ID() {return this.id;},
selector: '#person-icon', get Selector() {return this.selector;},
origWidth: $('#aboutme-section').css('width'), get OrigWidth() {return this.origWidth;},
origLeft: $('#aboutme-section').css('left'), get OrigLeft() {return this.origLeft;},
origRight: 'auto', get OrigRight() {return this.origRight;},
origHeight: $('#aboutme-section').css('height'), get OrigHeight() {return this.origHeight;},
origTop: $('#aboutme-section').css('top'), get OrigTop() {return this.origTop;},
origBottom: $('#aboutme-section').css('bottom'), get OrigBottom() {return this.origBottom;},
expandedWidth: '65%', get ExpandedWidth() {return this.expandedWidth;},
expandedLeft: $('#aboutme-section').css('left'), get ExpandedLeft() {return this.expandedLef;},
expandedRight: $('#aboutme-section').css('right'), get ExpandedRight() {return this.expandedRight;},
expandedHeight: '450px', get ExpandedHeight() {return this.expandedHeight;},
expandedTop: '65%', get ExpandedTop() {return this.expandedTop;},
expandedBottom: $('#aboutme-section').css('bottom'), get ExpandedBottom() {return this.expandedBottom;}};
var photographySection = {id: '#photography-tab', get ID() {return this.id;},
selector: '#camera-icon', get Selector() {return this.selector;},
origWidth: $('#photography-tab').css('width'), get OrigWidth() {return this.origWidth;},
origLeft: 'auto', get OrigLeft() {return this.origLeft;},
origRight: $('#photography-tab').css('right'), get OrigRight() {return this.origRight;},
expandedWidth: '40%', get ExpandedWidth() {return this.expandedWidth;},
expandedLeft: 'auto', get ExpandedLeft() {return this.expandedLeft;},
expandedRight: $('#photography-tab').css('right'), get ExpandedRight() {return this.expandedRight;}};
var gamesSection = {id: '#games-tab', get ID() {return this.id;},
selector: '#gamepad-icon', get Selector() {return this.selector;},
origWidth: $('#games-tab').css('width'), get OrigWidth() {return this.origWidth;},
origLeft: $('#games-tab').css('left'), get OrigLeft() {return this.origLeft;},
expandedWidth: '40%', get ExpandedWidth() {return this.expandedWidth;}};
Handlers
// Handles aboutme section functionality
function handleAboutMeSection(elemToAnimate, selectedElem, active, animateDuration=500, fadeInDuration=500, fadeOutDuration=250){
// First click
if (active){
// Animate vertically first
$(elemToAnimate.ID).animate({height: elemToAnimate.ExpandedHeight,
top: elemToAnimate.ExpandedTop,
bottom: elemToAnimate.OrigBottom}, animateDuration);
// Animate horizontally second
$(elemToAnimate.ID).animate({width: elemToAnimate.ExpandedWidth,
left: elemToAnimate.ExpandedLeft,
right: elemToAnimate.ExpandedRight}, animateDuration)
// Fade in content and remove active class
$(elemToAnimate.ID).find(".content").fadeIn(fadeInDuration);
$(selectedElem).removeClass('active');
// Second click
} else {
// Fade out content
$(elemToAnimate.ID).find(".content").fadeOut(fadeOutDuration, function(){
// Animate horizontally first
$(elemToAnimate.ID).animate({width: elemToAnimate.OrigWidth,
left: elemToAnimate.OrigLeft,
right: elemToAnimate.OrigRight}, animateDuration);
// Animate vertically second
$(elemToAnimate.ID).animate({height: elemToAnimate.OrigHeight,
top: elemToAnimate.OrigTop,
bottom: elemToAnimate.OrigBottom}, animateDuration)
});
// Add active class back in
$(selectedElem).addClass('active');
}
}
//Handles photography tab functionality
function handleTabs(elemToAnimate, selectedElem, active, animateDuration=500, fadeInDuration=500, fadeOutDuration=250){
// First click
if (active){
// Only animate horizontally
$(elemToAnimate.ID).animate({width: elemToAnimate.ExpandedWidth,
left: elemToAnimate.ExpandedLeft,
right: elemToAnimate.ExpandedRight}, animateDuration)
// Fade in content and remove active class
$(elemToAnimate.ID).find(".content").fadeIn(fadeInDuration);
$(selectedElem).removeClass('active');
// Second click
} else {
// Fade out content and only animate horizontally
$(elemToAnimate.ID).find(".content").fadeOut(fadeOutDuration, function(){
$(elemToAnimate.ID).animate({width: elemToAnimate.OrigWidth,
left: elemToAnimate.OrigLeft,
right: elemToAnimate.OrigRight}, animateDuration);
});
// Add active class back in
$(selectedElem).addClass('active');
}
}
Main
//Hide content initially
$(".content").hide();
//Handle click events
$(".image").click(function() {
//On first click
if ($(this).hasClass("active")) {
switch($(this).attr('id')) {
case 'person-icon':
handleAboutMeSection(aboutmeSection, aboutmeSection.Selector, true);
break;
case 'gamepad-icon':
handleTabs(gamesSection, gamesSection.Selector, true);
break;
case 'camera-icon':
handleTabs(photographySection, photographySection.Selector, true);
break;
default:
break;
}
// On second click
} else {
switch($(this).attr('id')) {
case 'person-icon':
handleAboutMeSection(aboutmeSection, aboutmeSection.Selector, false);
break;
case 'gamepad-icon':
handleTabs(gamesSection, gamesSection.Selector, false);
break;
case 'camera-icon':
handleTabs(photographySection, photographySection.Selector, false);
break;
default:
break;
}
}
});
});
You're right. It has a TL:DR feel to it.
However I think I know what you are going through so let me address your questions and just glimpse over the code:
1) What should I consider programmatically when taking the scalability of an app into account? How can this code be cleaned up/refined?
This depends on what you plan to do with it. Do you need to implement more blocks, maybe use tabs and blocks together to have several different options ? More important than that, do you wish to come back a few years later and don't slap yourself in the forehead, then yes, you probably could refactor this a bit.
Before I start, take this with a grain of salt. Every programmer is different, so my refactor examples may not tingle with others. If the code works, it is #right#. Only the code with errors is #wrong#. Again, another opinion of mine.
So, what I would do is create a settings literal object to deal with the section part, that contained what you needed. Lets take about me for example. Here's how I would do just the js code:
// Definitions ----------------------------------------
var section = {
elem: null,
selector: null,
content: null,
opened: false,
origDim: { },
currDim: { },
expdDim: { },
setup: function(settings) { // gets the settings and sets up the initial position
var self = this;
if ( !(
self.is_set(settings) ||
self.is_set(settings.elem) ||
self.is_set(settings.selector) ||
self.is_set(settings.content)
)
)
{
console.log('Your settings must send out an element, a content and a selector');
} else {
self.elem = settings.elem;
self.selector = settings.selector;
self.content = settings.content;
self.origDim = self.getPosition();
}
return self; // this allows chaining
},
// Sets up the range of motion the section will have
setRange: function(expdDim) {
var self = this;
if ( !(self.is_set(expdDim)) ) {
console.log('You have to provide a set of new positions.')
} else {
self.expdDim = {
width: (self.is_set(expdDim.width)?expdDim.width:self.currDim.width),
height: (self.is_set(expdDim.height)?expdDim.height:self.currDim.height),
top: (self.is_set(expdDim.top)?expdDim.top:self.currDim.top),
right: (self.is_set(expdDim.right)?expdDim.right:self.currDim.right),
bottom: (self.is_set(expdDim.bottom)?expdDim.bottom:self.currDim.bottom),
left: (self.is_set(expdDim.left)?expdDim.left:self.currDim.left)
};
}
return self; // this allows chaining
},
// Toggles from opened to close by listening to a property opened
toggle: function(animTime, fadeInTime, fadeOutTime) {
var self = this;
if (self.opened) self.close(animTime, fadeOutTime);
else self.open(self.expdDim, animTime, fadeInTime);
return self; // this allows chaining
},
// Expands the section
open: function(newDim, animTime, fadeInTime) {
var self = this;
if ( !(self.is_set(newDim)) ) console.log('You must send new dimensions!');
else {
var elem = $(self.elem);
elem
.animate(self.optionsVert(newDim), animTime)
.animate(self.optionsHorz(newDim), animTime)
.promise().done( function() {
$(this).find(self.content).fadeIn(fadeInTime)
self.currDim = self.getPosition();
self.opened = true;
});
}
return self; // this allows chaining
},
// Closes the section
close: function(animTime, fadeOutTime) {
var self = this;
var elem = $(self.elem);
// first fade
elem.find(self.content)
.fadeOut(fadeOutTime)
.promise()
.done(function(){
elem
.animate(self.optionsHorz(self.origDim), animTime)
.animate(self.optionsVert(self.origDim), animTime)
.promise()
.done( function() {
self.currDim = self.getPosition();
self.opened = false;
});
});
return self; // this allows chaining
},
// HELPER FUNCTIONS - these do not allow chaining - used as private functions
// Sets up original dimensions based on the element
getPosition: function() {
var self = this;
var offset = $(self.elem).offset();
var posDim = {
width: $(self.elem).width()+'px',
height: $(self.elem).height()+'px',
top: offset.top+'px',
right: parseInt(offset.left)+parseInt($(self.elem).width())+'px',
bottom: parseInt(offset.top)+parseInt($(self.elem).height())+'px',
left: offset.left+'px'
};
return posDim;
},
// validates if a given variable is set
is_set: function(vary) {
return (typeof vary != 'undefined');
},
// returns a subset of dimension variables belonging to the X plane
optionsHorz: function(newDim) {
return {
width: newDim.width,
left: newDim.left,
right: newDim.right
};
},
// returns a subset of dimension variables belonging to the Y plane
optionsVert: function(newDim) {
return {
height: newDim.height,
top: newDim.top,
bottom: newDim.bottom
};
}
};
// Definitions ----------------------------------------
$(document).ready(function() {
// Setting up section about me
var aboutme = section;
aboutme.setup({
elem: '#aboutme-section',
selector: '#person-icon',
content: '.content'
}).setRange({
width: '65%',
height: '450px',
top: '65%'
});
//Hide content initially
$(".content").hide();
//Handle click events
$(".image").click(function() {
switch($(this).attr('id')) {
case 'person-icon':
aboutme.toggle(500,500,250);
break;
default:
break;
}
});
});
This is how you can templatize your sections. You can augment this block to augment it's functionality but, if you take a look, the settings get pretty thin and simple.
Hope this helps.
Related
I'm trying to create a sliding sidebar and was wondering if there was a better way then what I am doing already.
<img id = "MenuIcon" src = "MenuIcon.png" alt = "Menu Icon" onclick = "slideSideBar()" />
At the moment I simply check if the sideSlideCount is even - if it is the sidebar must not be out, so when the function is called it slides out; if sideSlideCount is odd (i.e. % 2 != 0) then the sidebar should slide out of view.
var sideSlideCount = 0; // variable used with the side bar
var bodyHeight = $(window).height();
var bodyWidth = $(window).width();
console.log(bodyWidth);
function slideSideBar() {
if (sideSlideCount % 2 == 0) { // if sideSlideCount is even, i.e. the side bar is hidden
$("#SideBar").animate({width: bodyWidth / 6}, 600); // slide the bar out to 300 width, 0.6 seconds
$("#SideLinks").fadeTo(1000, 0.8); // fade the links into view, 1 second, to 100% opacity
}
else { // else, if the side bar is in view
$("#SideBar").fadeIn(300).animate({width: 0}, 600); // slide the bar back out of view (0 width), 0.6 seconds
$("#SideLinks").fadeTo(200, 0); // fade the links out of view, 0.2 seconds, to 0% opacity
}
sideSlideCount++; // increment the variable
}
You could simply make your code modular to avoid global variables. You should be looking into AMD modules, however to keep it simple you can create yourself a namespace where your code will live.
DEMO: http://jsfiddle.net/BzYuQ/
//Define a SlidingSidebar reusable component
!function (ns, $) {
function SlidingSidebar(el, animateDuration) {
this.$el = $(el);
this.animateDuration = animateDuration;
}
SlidingSidebar.prototype = {
constructor: SlidingSidebar,
toggle: function () {
this.$el.stop().animate({ width: 'toggle' }, this.animateDuration);
}
};
ns.SlidingSidebar = SlidingSidebar;
}(slideExampleSite = window.slideExampleSite || {}, jQuery);
$(function () {
//The following behavior could also be in a configurable "SlidebarFeature" module
//or you could integrate the button directly into the SlidingSidebar.
//I'm just showing how code can be modularized here.
var sideBar = new slideExampleSite.SlidingSidebar('#sidebar', 600);
$('#toggle-btn').click(function () {
sideBar.toggle();
});
});
Obviously in this case the SlidingSidebar component doesn't do much, but as your application grows, modularizing your code to get away from the $(function () {/*tons of code*/}); anti-pattern will pay off in many ways.
You didn't say what "better" is, but if the intention is just to avoid globals, you can use a closure:
var slideSideBar = (function() {
var sideSlideCount = false; // variable used with the side bar
var bodyHeight = $(window).height();
var bodyWidth = $(window).width();
console.log(bodyWidth);
// This function has access to all the above variables, but no other
// function does.
function slideSideBar() {
if (sideSlideCount) {
$("#SideBar").animate({width: bodyWidth / 6}, 600);
$("#SideLinks").fadeTo(1000, 0.8);
} else {
$("#SideBar").fadeIn(300).animate({width: 0}, 600);
$("#SideLinks").fadeTo(200, 0);
}
sideSlideCount = !sideSlideCount;
}
// Return a reference to the slideSideBar function so it's available
// globally, but access to variables is "private"
return slideSideBar;
}());
The only difference is that slideSideBar won't exist until the above has been executed, so don't try to call it until afterward.
Not sure on your definition of "better", but I see your using jQuery which has a nice $toggle feature which can help here.
function slidesideBar(){
$("#SideBar").toggle(function() {
$(this).animate({width: bodyWidth / 6}, 600); // slide the bar out to 300 width, 0.6 seconds
$("#SideLinks").fadeTo(1000, 0.8); // fade the links into view, 1 second, to 100% opacity
}, function() {
$(this).fadeIn(300).animate({width: 0}, 600); // slide the bar back out of view (0 width), 0.6 seconds
$("#SideLinks").fadeTo(200, 0); // fade the links out of view, 0.2 seconds, to 0% opacity
});
}
Credit: jQuery Animation Toggle. Copy and paste:
When given a list of functions as arguments, the .toggle(fn1, fn2) method will alternate between the functions given starting with the first one. This automatically keeps track of the toggle state for you - you don't have to do that.
jQuery doc is here. There are multiple forms of .toggle() depending upon the arguments used so you don't always find the right one when searching the jQuery doc.
I am doing some research at the moment into creating a new maths game for primary school children where divs from 0-9 appear at random inside a container.
A question is given at the beginning. Something like, multiples of 20. The user will then have to click on the correct ones, and they will then be counted at the end and a score will be given.
I have just changed the speed in which the divs appear so that they appear for longer and more than one at a time to make the game easier for younger children.
I used "fadeIn" like so..
$('#' + id).animate({
top: newY,
left: newX
}, 'slow', function() {}).fadeIn(2000);
}
My problem is that now when I shoot the correct or incorrect number the animation is very glitchy and I cannot figure out why.
Fiddle: http://jsfiddle.net/cFKHq/6/ (See version 5 to see what it was like before)
Inside startplay(), control the concurrency when calling scramble() , I do it with a global var named window.cont, so I replaced your following call:
play = setInterval(scramble, 1800);
for this one:
play = setInterval(function() {
if (window.cont){
window.cont = false;
scramble();
}
}, 1000);
The var window.cont needs to be set globally at the start of your code, like so:
var miss = 0;
var hit = 0;
var target = $("#target");
window.cont = true;
So with window.cont you now can control that animations are executed one after another, without overlapping, like so:
$('#'+id).css({
top: newY,
left: newX
}).fadeIn(2000, function() {
setTimeout(function() {
$('#' + id).slideUp('fast');
window.cont = true;
}, 1500);
});
See working demo
I'm coding this content slider using JavaScript/Jquery.
Please have a look at my code on JSFiddle: http://jsfiddle.net/46JfZ/7/
cn = {
hero:function(r,lx,rx,fx,fs,ss,a){
rc=$(r).children(), rcw=rc.width(), rca=rc.size(), rw=rcw*rca, p='px'; $(r).css({'width':rw})
$(lx).click(function(){
n=$(r).position().left-rcw;
switch(fx){
case 'slide':$(r).stop(true,true).animate({left:(n<-rw+rcw?0:n)+p},ss,'easeOutExpo'); break;
case 'fade':$(r).stop(true,true).fadeOut(fs).animate({left:(n<-rw+rcw?0:n)+p},1).fadeIn(fs); break;
}
})
$(rx).click(function(){
n=$(r).position().left+rcw;
switch(fx){
case 'slide':$(r).stop(true,true).animate({left:(n>0?-rw+rcw:n)+p},ss,'easeOutExpo'); break;
case 'fade':$(r).stop(true,true).fadeOut(fs).animate({left:(n>0?-rw+rcw:n)+p},1).fadeIn(fs); break;
}
})
if(a.match('yes')){
$('#hero').append('<div id="indicate"></div>');
$.each(rc, function(){$('#indicate').append('<span></span>');})
function indicate(){
$('#indicate span').removeClass('active');
for(i=1;i<=rca; i++){
o=i-1,
newReelpos = - rcw*o,
q=$(r).position().left;
if(q == newReelpos ){
$('#indicate span:nth-child('+ i +')').addClass('active');
}
}
$(lx).click();
}
setInterval(indicate,3000);
$(lx).parent().hide();
}
}
}
$(function(){
cn.hero('#reel', '#next', '#prev', 'slide', 300, 600, 'yes');
});
I've missed something because the status dots(blue & green dots on the right) aren't working properly?
Any help would be appreciated greatly, Thank you
There are some things you're doing that aren't great or are wrong. For instance:
function(r,lx,rx,fx,fs,ss,a)
This is not a good way to name your arguments (or variables). What does all that mean? You might be asking yourself this in a few months as you try to fix something that has broken. Use descriptive argument, variable and function names.
You (inadvertently?) use global variables. For instance:
rc=$(r).children(), rcw=rc.width(), rca=rc.size(), // etc.
These are all essentially global. Since you have a closure, you can prepend a var on each of those and they will stay in scope for your handlers and interval calls.
Your code formatting is problematic, almost (in my opinion) guaranteeing you'll have code that's hard to visually inspect for problems and inefficiencies (especially combined with the above and the below other observations). Space your code out. For instance:
rc=$(r).children(), rcw=rc.width(), rca=rc.size(), rw=rcw*rca, p='px'; $(r).css({'width':rw})
Could be:
var $r = $(r),
rc = $r.children(),
rcw = rc.width(),
rca = rc.size(),
rw = rcw * rca,
p = 'px';
Now I ask you, which do you think is easier to read?
Note, you also have several spots like this where a statement has no ending ;. Do not get in the habit of doing this, as it's poor practice and can lead to problems later on that can be hard to spot. Yes, I know Javascript allows you to do this, but don't take the bug bait.
You also should be caching jQuery calls. So all of your $(r), which occur within the same closure, could (I argue in the case of animation, should) be:
var $r = $(r); // First, cache
...
$r.stop(true,true).animate() // Then use
This will speed up your code, since jQuery is doing less DOM traversals to find the element(s) you're working on.
Taking all that into consideration, your actual problem is that you're not calculating the dot selection very effectively (which appears to be, find the current left value and then iterate over the width of each cell, testing if the width matches the left value). Note, math is not my strong suit, so I imagine there's a more efficient/direct way of handling the underlying math, but doing a loop to find the "active" dot is not necessary.
My approach:
cn = {
hero: function(r,lx,rx,fx,fs,ss,a){
var $r = $(r),
rc = $r.children(),
rcw = rc.width(),
rca = rc.size(),
rw = rcw * rca,
p = 'px';
$r.css({width: rw});
$(lx).click(function(){
var n = $r.position().left - rcw;
switch(fx){
case 'slide':
$r.stop(true,true)
.animate({
left: (n<-rw+rcw?0:n)+p
}, ss);
break;
case 'fade':
$r.stop(true,true)
.fadeOut(fs)
.animate({
left: (n<-rw+rcw?0:n)+p
}, 1)
.fadeIn(fs);
break;
}
});
$(rx).click(function(){
var n = $r.position().left + rcw;
switch(fx){
case 'slide':
$(r).stop(true,true)
.animate({
left: (n>0?-rw+rcw:n)+p
}, ss);
break;
case 'fade':
$r.stop(true,true)
.fadeOut(fs)
.animate({
left: (n>0?-rw+rcw:n)+p
}, 1)
.fadeIn(fs);
break;
}
});
// I have no idea why you're using match; wouldn't a boolean true work?
if (a.match('yes')) {
$('#hero').append('<div id="indicate"></div>');
$.each(rc, function(){
$('#indicate').append('<span></span>');
});
function indicate(fire){
var $indicates = $('#indicate span'),
left = parseInt($r.position().left),
count = $indicates.length;
var i = (left / -rcw) + 2;
if (i > count || fire !== true) {
i = 1;
}
$indicates
.removeClass('active')
.parent()
.find('span:nth-child('+ i +')')
.addClass('active');
if (fire) {
$(lx).click();
}
setTimeout(function(){
indicate(true);
}, 3000);
}
indicate(false);
$(lx).parent().hide();
}
}
}
$(function(){
cn.hero('#reel', '#next', '#prev', 'slide', 300, 600, 'yes');
});
http://jsfiddle.net/kEG2p/
I'm trying to move a div to one side or the other depending on it's position, BUT! no matter what method I use to chance its position, it doesn't really change.
var $ancho = $(document).width();
var $posP = $("#portafolio").offset().left;
var $porP = Math.floor($posP * 100 / $ancho);
the var porP saves the percentage of the div's (portafolio) position
Now, the initial position of portafolio is 13(%)
if($porP == '13')
{
$("#portafolio").mouseenter(function(){
$("#imge").stop().fadeTo('500', 0.3, function() {});});
$("#portafolio").mouseleave(function(){
$("#imge").stop().fadeOut('500', function() {});
});
$("#portafolio").click(function(){
$("#portafolio").animate({
left: "+=87%",},
{ queue: false, duration: 900, easing:'swing' });
$("#imge").fadeTo('500', 0.3, function() {
});
$(iframe).attr('src','portafolio.html');
$("#marco").fadeTo('500', 1 , function() {});
$(iframe).open();
});
}
but even when I move portafolio to the right, in the animation function, the porP var still shows 13, therefore it never reaches the else
else
{ move to the other side }
I already tried to assign a new value to the porP var inside the click function but to no avail. It just always executes the IF statement.
Does someone know why the value (porP) doesn't change? and how can I actually change it to make the else finally work?
I searched everywhere and tried everything that came to my mind with my very limited knowledge of jquery and got to nowhere. Please!!
edit// #portafolio is a relative positioned div to its parent #contenido which is absolute positioned and is left 20px . #portafolio is left 15% . I don't know if that has something to do.
Alice, I'm not sure exactly what you're trying to do, but the if and else statements probably should be inside the 'click()' handler. Something like,
$("#portafolio").click(function(){
var $ancho = $(document).width();
var $posP = $("#portafolio").offset().left;
var $porP = Math.floor($posP * 100 / $ancho);
if($porP == '13')
{
$("#portafolio").animate({
left: "+=87%",},
{ queue: false, duration: 900, easing:'swing' });
$("#imge").fadeTo('500', 0.3, function() {
});
$(iframe).attr('src','portafolio.html');
$("#marco").fadeTo('500', 1 , function() {});
$(iframe).open();
}
else
{ move to the other side }
});
Slashdot has a little widget that allows you to tweak your comment threshold to filter out down-modded comments. It will be in one place if you scroll to the top of the page, and as you scroll down, at some point, where its original home is about to scroll off the page, it will switch to fixed position, and stay on your screen. (To see an example, click here.)
My question is, how can I accomplish the same effect of having a menu be in one place when scrolled up, and switch to fixed position as the user scrolls down? I know this will involve a combination of CSS and javascript. I'm not necessarily looking for a full example of working code, but what steps will my code need to go through?
Okay, I figured it out. I will post it here in case it help anyone else. This solution uses prototype, and an internal library that gives me the registerEvent, getElementX and getElementY functions, which do what you would think.
var MenuManager = Class.create({
initialize: function initialize(menuElt) {
this.menu = $(menuElt);
this.homePosn = { x: getElementX(this.menu), y: getElementY(this.menu) };
registerEvent(document, 'scroll', this.handleScroll.bind(this));
this.handleScroll();
},
handleScroll: function handleScroll() {
this.scrollOffset = document.viewport.getScrollOffsets().top;
if (this.scrollOffset > this.homePosn.y) {
this.menu.style.position = 'fixed';
this.menu.style.top = 0;
this.menu.style.left = this.homePosn.x;
} else {
this.menu.style.position = 'absolute';
this.menu.style.top = null;
this.menu.style.left = null;
}
}
});
Just call the constructor with the id of your menu, and the class will take it from there.
Thanks for the effort of sharing this code.
I made some small changes to make it work with the current release of Prototype.
var TableHeaderManager = Class.create({
initialize: function initialize(headerElt) {
this.tableHeader = $(headerElt);
this.homePosn = { x: this.tableHeader.cumulativeOffset()[0], y: this.tableHeader.cumulativeOffset()[1] };
Event.observe(window, 'scroll', this.handleScroll.bind(this));
this.handleScroll();
},
handleScroll: function handleScroll() {
this.scrollOffset = document.viewport.getScrollOffsets().top;
if (this.scrollOffset > this.homePosn.y) {
this.tableHeader.style.position = 'fixed';
this.tableHeader.style.top = 0;
this.tableHeader.style.left = this.homePosn.x;
} else {
this.tableHeader.style.position = 'absolute';
this.tableHeader.style.top = null;
this.tableHeader.style.left = null;
}
}
});
For a demo but not based on the code above checkout:
fixed-floating-elements