I have a menu composed of three options.
Clicking on one causes a container div to "FadeInDown".
Then, its contents "FadeIn".
Clicking on another menu item or anywhere else on the page causes the
contents to "FadeOut" then container div to "FadeOutUp".
Here is the fiddle that I have been testing jsfiddle
$(document).ready(function() {
$('.container').each(function() {
animationHover(this,'.fadeInDown');
});
});
I'm not very familiar with jQuery and have been trying to use animate-css to get me along. Thanks for any help and tips in advance and welcome coding criticism :)
My answer is mainly based on JQuery and its animate function (http://api.jquery.com/animate/) . Here is the fiddle :
http://jsfiddle.net/awmat/7/
I use JavaScript objects like fadeInDown to animate the container.
var fadeInDown = {
opacity:1,
top: "50px"
};
And i use the complete callback function of animate to make the content appear after the container.
To manage several div (one for each menu item), I use id as selectors, but since the "click and display" function remains the same, I used a "builder" : (this uses a closure, so if you're not familiar with JavaScript, you may have to read several times to understand what is going on)
var menuClickCallbackBuilder = function(menuItem){
var container = $('#container' + menuItem);
var content = container.find('.content');
var showContent = function(){
content.animate({opacity:1},{duration:1000});
};
return function() {
var activeContainer = $('.active');
var hideContainer = function(){
activeContainer.animate(fadeOutUp,1000);
};
activeContainer.find('.content').animate({opacity:0},{duration: 1000, complete : hideContainer});
activeContainer.removeClass("active");
if(activeContainer[0] != container[0])
{
var timeout = activeContainer[0] ? 2000 : 0 ;
setTimeout(function(){
container.animate(fadeInDown,{duration : 1000, complete :showContent});
},timeout);
container.addClass("active");
}
}
};
This way, when you add the add the click callbacks, you can just do :
$(document).ready(function(){
// note that menuClickCallbackBuilder(1) returns a function
// again if you're not familiar with JS, you may have to re-read menuClickCallbackBuilder
$('#menuLink1').on('click', menuClickCallbackBuilder(1));
$('#menuLink2').on('click', menuClickCallbackBuilder(2));
$('#menuLink3').on('click', menuClickCallbackBuilder(3));
});
Some improvements you can bring to this :
Factor the durations into a variable (e.g animationDurationInSeconds) so that if you want to change the speed of the animation, you only have 1 thing to change. (#Huangism: and right after you did that, make animation faster so that it gets more dynamic)
(From #Huangism) : stop it from going crazy when people cicks on the menu 10 times really fast
Actually, I think you don't need 3 different containers, you could do it with just one container (though I don't know if it would be considered an improvement)
There is probably a way to use CSS classes instead of fadeInDown and fadeOutUp JS objects. That would be cleaner, I think you should keep styles in CSS as much as you can.
There is no need for different IDs for menu items, you could do the exact same thing with a loop.
Whatever your imagination wants to add
Related
I've got a container that includes several icons the user can hover over and be shown a block of text next to it. I'm grabbing the blocks of text from an array and have a randomize function so that they're always shown a different block of text when revisiting the page.
I ran into an issue where every time you hover over an icon, it keeps adding more array elements, because the function gets called each time you hover over the icon. So I decided to use the one() method so the function only runs once, however that's where my real issue is. Using the one() method doesn't show ANY text, and I'm pretty sure it's due to the nested function I have.
You can test this out here: http://www.evanvolmering.com/bootstrap/docs/examples/carousel/eyeswideshut.html
In the banner a video will play, and shortly into it a little icon will appear in the bottom of left of the banner. Hovering over it will show some text. When you hover over it again it adds another array item, and so on. It works, but I don't want it to keep adding array items.
10 seconds later another icon will appear to the top right, which currently has the one() method applied to it. As you can see nothing happens when you hover over it. Not sure where to go from here.
My randomize code (which I got from another StackOverflow answer):
var numRandoms = 14;
function makeUniqueRandom() {
if (!uniqueRandoms.length) {
for (var i = 0; i < numRandoms; i++) {
uniqueRandoms.push(i);
}
}
var index = Math.floor(Math.random() * uniqueRandoms.length);
var val = uniqueRandoms[index];
uniqueRandoms.splice(index, 1);
return val;
}
My code which currently 'works' but keeps adding more array items on hover:
$('img.button1').hover(function(){
$('p.trivia1').fadeIn("slow");
$( 'p.trivia1' ).append(makeUniqueRandom());
},
function(){
$("p.trivia1").stop().fadeOut("slow");
});
My code that uses one() but doesn't do anything on hover:
$('img.button2').one("hover",function(){
$('p.trivia2').fadeIn("slow");
$( 'p.trivia2' ).append(makeUniqueRandom());
},
function(){
$("p.trivia2").stop().fadeOut("slow");
});
Use mouseenter/mouseleave instead of hover
$('img.button1').on('mouseenter',function(){
$('p.trivia1').fadeIn("slow");
$( 'p.trivia1' ).append(makeUniqueRandom());
}).on('mouseleave',function(){
$("p.trivia1").stop().fadeOut("slow");
});
I wanted to create side tab slide outs for two subdivisions of our company on the new version of the website I'm working on. After a lot of research, I decided to make it relatively simple (or so I thought).
I created a div (#CMTabWrap, 245px) that wrapped around two other divs: #CMContent (200px) and #BNOCM_tab (45px). I set the margin for #CMTabWrap to -200px (the width of #CMContent), leaving only the tab showing. The HTML and CSS are working fine.
Beneath the divs in my HTML, I put the code below. On click, it should test the class of the object and move it in or out (as appropriate) by the width of the content, either making the whole kit and kaboodle visible or reducing it to just the tab.
I want to say that I ran the code through JSLint, and read several questions here along the same lines. I applied some of the fixes, but still nothing is happening. I have a feeling it's something obvious -- I'm not quite intermediate with JS and slightly more than a noob. Any help appreciated.
<script type="text/javascript">
/*global $, jQuery, alert*/
$(function(){
$("#BNOCM_tab").click(function () {
$('#BNOCM_tab').addClass('in');
var contentWidth = $('#CMContent').width();
if ($(this).is('.out')) {
$(this).removeClass('out').addClass('in');
$("#CMTabWrap").animate(
{"left": '+=' + contentWidth},
"slow"
);
}
if ($(this).is('.in')) {
$(this).removeClass('in').addClass('out');
$("#CMTabWrap").animate(
{"right": '-=' + contentWidth},
"slow"
);
});
});
</script>
Inside your click-function this refers to the clicked element, so $(this) and $("#BNOCM_tab") is always the same. Also your second if should be an else, because now you just swap the classes from "in" to out as #wwwmarty pointed to. So yours (inside $(document).ready) could look like this:
<div id="BNOCM_tab" class="out"></div> // add class 'out to the tab
var $Wrap = $("#CMTabWrap"), // create a jQuery object only once, store it
$Tab = $("#BNOCM_tab");
$Tab.click(function () {
var contentWidth = $('#CMContent').width();
// use .hasClass() for class check, note there's no dot in the name
if ($Tab.hasClass('out')) {
$Tab.removeClass('out').addClass('in');
$Wrap.animate({left: '+=' + contentWidth}, "slow");
} else {
$Tab.removeClass('in').addClass('out');
$Wrap.animate({left: '-=' + contentWidth}, "slow");
};
});
If that won't work, add the relevant part of your html to your question or/and make a fiddle and post what exactly went wrong.
Heyho,
I´am working on a project in my university and I´d like to use "Hammer.js".
I´ve downloaded the Carousel-Example and it works perfectly for me.
But I would like to start a the middle pane of my code and it´s not so simple I think.
It´s something like this:
http://img203.imageshack.us/img203/6326/schemeas.jpg
so Hammer.js starts always with the green screen. But I like to start with the yellow one.
I´ve added one swipe right to the init function but it looks horrible when the page is loading and could not be the goal ^^
I hope anyone of you have an idea how to solve my problem.
Try calling
carousel.showPane(1);
That will display the second pane instantly. You will want to put this near the bottom, right after where it says.
carousel.init();
If you're feeling adventurous you could try and make it automatically start with that pane as there's a variable inside the Carousel function called current_pane which is set to a default of 0 (the first pane). Altering this may work too but might require more code somewhere else. Experiment!
edit
NULL is right, it does animate it. Here's a more in depth method to set it without animation:
I found that the method responsible for changing which pane is showing was the setContainerOffset mthod which could be passed a variable to animate it. I previously told you to use showPane(2) but that then called
setContainerOffset(offset, true)
which caused the animation occur. What you should do instead is make a slightly different version of showPane...
this.setPane = function( index ) {
// between the bounds
index = Math.max(0, Math.min(index, pane_count-1));
current_pane = index;
var offset = -((100/pane_count)*current_pane);
setContainerOffset(offset, false);
};
You'll find it's almost identical to showPane except for the name and the fact that it calls setContainerOffset with animation: false. This will immediately show the pane of your choice and can be called using
carousel.setPane(index);
What I've done is added this to the init function so that it looks like this:
this.init = function() {
setPaneDimensions();
var c = this;
$(window).on("load resize orientationchange", function() {
setPaneDimensions();
c.setPane(current_pane);
//updateOffset();
})
};
Now you can change
var current_pane = 0;
to whatever you want and the carousel will always start with that pane when it's initialised! simple!
I've a scenario that requires me to detect animation stop of a periodically animated element and trigger a function. I've no control over the element's animation. The animation can be dynamic so I can't use clever setTimeout.
Long Story
The simplified form of the problem is that I'm using a third party jQuery sliding banners plugin that uses some obfuscated JavaScript to slide banners in and out. I'm in need of figuring out a hook on slideComplete sort of event, but all I have is an element id. Take this jsfiddle as an example and imagine that the javascript has been obfuscated. I need to trigger a function when the red box reaches the extremes and stops.
I'm aware of the :animated pseudo selector but I think it will need me to constantly poll the required element. I've gone through this, this, and this, but no avail. I've checked jquery promise but I couldn't figure out to use that in this scenario. This SO question is closest to my requirements but it has no answers.
P.S. Some more information that might be helpful:
The element isn't created by JavaScript, it is present on page load.
I've control over when to apply the plugin (that makes it periodically sliding banner) on the element
Most of the slideshow plugins I have used use changing classes at the end of the animation... You could extend the "addClass" method of jQuery to allow you to capture the class change as long as the plugin you use is using that method like it should:
(function($){
$.each(["addClass","removeClass"],function(i,methodname){
var oldmethod = $.fn[methodname];
$.fn[methodname] = function(){
oldmethod.apply( this, arguments );
this.trigger(methodname+"change");
return this;
}
});
})(jQuery);
I threw together a fiddle here
Even with obfuscated code you should be able to use this method to check how they are sending in the arguments to animate (I use the "options" object when I send arguments to animate usually) and wrap their callback function in an anonymous function that triggers an event...
like this fiddle
Here is the relevant block of script:
(function($){
$.each(["animate"],function(i,methodname){
var oldmethod = $.fn[methodname];
$.fn[methodname] = function(){
var args=arguments;
that=this;
var oldcall=args[2];
args[2]=function(){
oldcall();
console.log("slideFinish");
}
oldmethod.apply( this, args );
return this;
}
});
})(jQuery);
Well since you didn't give any indication as to what kind of animation is being done, I'm going to assume that its a horizontal/vertical translation, although I think this could be applied to other effects as well. Because I don't know how the animation is being accomplished, a setInterval evaluation would be the only way I can guess at how to do this.
var prevPos = 0;
var isAnimating = setInterval(function(){
if($(YOUROBJECT).css('top') == prevPos){
//logic here
}
else{
prevPos = $(YOUROBJECT).css('top');
}
},500);
That will evaluate the vertical position of the object every .5 seconds, and if the current vertical position is equal to the one taken .5 seconds ago, it will assume that animation has stopped and you can execute some code.
edit --
just noticed your jsfiddle had a horizontal translation, so the code for your jsfiddle is here http://jsfiddle.net/wZbNA/3/
I'm trying to use the browser scroll event to place a block of html based on the amount a user has scrolled. The code works but it is causing a huge performance issue which basically forces my browser to freeze.
Any insight as to why and what I could do to resolve this would be greatly appreciated.
<script type="text/javascript">
$('#content').scroll(function () {
var scroll = $('#content').scrollTop();
var $controls = $(".controls").clone();
if (scroll > 200) {
$(".controls").remove();
$('#header').append($controls);
}
else {
$(".controls").remove();
$('.banner').append($controls);
}
});
</script>
First, discovering elements in the DOM is an expensive activity, so cache your jQuery objects.
Second, .append() moves elements around so .clone() and remove() should be unnecessary.
This gives :
var $$ = {//cache of jQuery objects
content: $('#content'),
controls: $(".controls"),
header: $("#header"),
banner: $('.banner')
};
$('#content').scroll(function() {
$controls.appendTo(($$.content.scrollTop() > 200) ? $$.header : $$.banner);
});
Now, you can work on reducing the frequency at which the handler is called, which can be achieved as follows :
var $$ = {//cache of jQuery objects
content: $('#content'),
controls: $(".controls"),
header: $("#header"),
banner: $('.banner')
};
var scrollHandling = {
allow: true,
reallow: function() {
scrollHandling.allow = true;
},
delay: 50 //(milliseconds) adjust to the highest acceptable value
};
$('#content').scroll(function() {
if(scrollHandling.allow) {
$controls.appendTo(($$.content.scrollTop() > 200) ? $$.header : $$.banner);
scrollHandling.allow = false;
setTimeout(scrollHandling.reallow, scrollHandling.delay);
}
});
The scroll function is called for every movement of the scrollbar. That can potentially be a lot of times, so you need to be careful how much code you are running and certainly how much manipulation of the DOM you are doing.
In this case, you'll be repeating a lot of the same actions (clone, append, remove) as the scrolling is occurring, but it appears that you only want to flip between two states as you cross back and forth over that 200 scroll value. You could potentially solve most of the performance issues with something like this:
<script type="text/javascript">
var thresholdCrossed = false;
$('#content').scroll(function () {
var scroll = $('#content').scrollTop();
var threshold = 200;
if (scroll > threshold && !thresholdCrossed) {
var controls = $(".controls").clone();
$(".controls").remove();
$('#header').append(controls);
} else if (scroll <= threshold && thresholdCrossed) {
var controls = $(".controls").clone();
$(".controls").remove();
$('.banner').append(controls);
}
thresholdCrossed = scroll > threshold;
});
</script>
You can do additional work that some of the other answers describe to help reduce wasted resources, but this should give you a general idea of how to hopefully help the main performance concern of constantly modifying the DOM as you scroll. I would probably suggest some combination of this along with the answer provided by #Kolink so that you are truly limiting the DOM manipulation to the least amount necessary.
You are cloning all .controls elements every tick of scrolling, even when it is not needed.
I would suggest cloning the controls on ready and setting it to display:none. Then, just toggle the display based on the scroll position.
On re-reading your question, it looks like you're just moving the controls element from the header to the banner? In that case, you don't even need a clone. However I strongly suggest adding id="controls" to the controls element, and id="banner" - use IDs instead of classes in general if there is only one.
document.getElementById('content').onscroll = function() {
document.getElementById(this.scrollTop > 200 ? "banner" : "header")
.appendChild(document.getElementById('controls'));
};
Every time the scroll bar moves jQuery has to go into the DOM to get the variables you references. To start cache the variables so jQuery doesnt have to do twice the work.
var content = $('#content');
content.scroll(function(){
});