Thought I'd post here. My first hour on jQuery, actually first programing ever done. Would love to learn whats not right and how it could be better.
$(function() {
function hide_me()
//A place to specify which elements you want hidden, on page load.
{
$("li.credentials").hide();
}
function first_bow()
//The div right_column takes a bow on initial load.
{
$("div#right-column").show("drop");
}
function bigpeek()
//The third column toggles in/out. All elements under div right_column.
{
$("div#right-column").toggle("drop", "fast");
}
function smallpeek()
//Smaller snippets like credentials or user assignment flying in/out.
{
$("li.credentials").toggle("drop", "fast");
}
$(document).ready(function() {
$("*").ready(hide_me);
$("*").ready(first_bow);
$(".btn-new-email").click(bigpeek);
$(".button").click(smallpeek);
$(".icon-delete").mouseover(function() {
$(this).effect("bounce", "fast");
});
});
});
The best thing to learn about programming is how to effectively re-use code. In your code, you have set up some functions that you yourself claim will do a bunch of the same thing. So instead, you could make it better by only writing code to do the repeated task once.
For one example, instead of creating a function where you place a bunch of things that need to be hidden, I would add a class to the elements that should be hidden, and then hide all those elements:
function hide_me()
//Hides anything with the "hide-me-onload" class
{
$(".hide-me-onload").hide();
}
$(function () {
...
}
is the same as
$(document).ready(function() {
...
}
So you can move the method calls from inside your $(document).ready() to be inside your $(function(){}). Also try to use IDs instead of class names wherever possible. Something like this will go through the entire DOM to look for an element
$(".item")
Be more specific
$("#itemID") // use IDs instead of Classes
//If you have to use class name then you can speed up the selector by adding the element tag before it
$("div.item")
Using $("*").ready() within $(document).ready() is redundant... you already know using all of the elements are ready! Also, in general using the universal selector $('*') is very inefficient.
So, the first two lines of your $(document).ready() can just be:
hide_me();
first_bow();
Other than that and a couple of issues with organization and nomenclature you're off to a great start, keep it up!
Related
I want to replay my jquery function ChangeStats() every 5 seconds, it's currently doing sod all.
function ChangeStats() {
$('body').find('.admin-stats-big-figures-hidden').fadeIn(500);
setTimeout(function() {
$('body').find('.admin-stats-big-figures').fadeOut(500);
}, 500);
}
$(document).ready(function(){
setInterval(ChangeStats, 5000);
})();
Yes I have got the right class names.
No I haven't used underscores in my HTML.
I think it's something to do with my use of "find()", once the DOM has loaded and the function is set is it meant to traverse up the DOM tree instead of down?
EDIT:
Updated code, still not working.
HTML:
<span class="admin-stats-big-figures">%productCount%</span>
<span class="admin-stats-big-figures-hidden">hey</span>
Ok, I am going to go out on a limb and make several assumptions here; one is that you wish to cycle between two elements repeatedly, another is that you are using $(this) in the context of the window rather than a containing element. If either of these are incorrect then the following solution may not be suitable. However, let's give this a shot, eh?
1) You need to use setInterval rather than setTimeout to create a repeating call. You can of course "chain" your timeouts (ie: call the succeeding timeout from the code of the current timeout). This has some benefits in certain situations, but for now let's just assume you will use intervals rather than timeouts.
2) You call the find() jQuery method every time, which is a little unnecessary, especially if you will be repeating the actions so one idea would be to cache the lookup. If you are going to do that a custom object would be more suitable than separate global variables.
3) Some flexibility in terms of starting and stopping the animation could be provided. If we use a custom object as mentioned in (2) then that can easily be added.
4) You are using fadeIn and fadeOut, however if you wish the items to cycle then fadeToggle may be your best solution as it will simply allow you to do exactly that, toggle, without needing to check the current opacity state of the element.
5) Finally in my example I have provided a little extra "padding HTML" in order for the example to look good when run. Fading in jQuery will actually set the faded item to a CSS display of "none" which results in the content "jumping about" in this demo, so I have used some div's and a couple of HTML entity spaces to keep the formatting.
Ok, after all that here is the code..
// your custom animation object
var myAnim = {
// these will be cached variables used in the animation
elements : null,
interval : null,
// default values for fading and anim delays are set to allow them to be optional
delay : { fade: 500, anim: 200 },
// call the init() function in order to set the variables and trigger the animation
init : function(classNameOne, classNameTwo, fadeDelay, animDelay) {
this.elements = [$("."+classNameOne),$("."+classNameTwo)];
// if no fade and animation delays are provided (or if they are 0) the default ones are used
if (animDelay) this.delay.anim = animDelay;
if (fadeDelay) this.delay.fade= fadeDelay;
this.elements[0].fadeOut(function(){myAnim.start()});
},
// this is where the actual toggling happens, it uses the fadeToggle callback function to fade in/out one element once the previous fade has completed
update : function() {
this.elements[0].fadeToggle(this.delay.anim,function(el,delay){el.fadeToggle(delay)}(this.elements[1],this.delay.anim));
},
// the start() method allows you to (re)start the animation
start : function() {
if (this.interval) return; // do nothing if the animation is currently running
this.interval = setInterval(function(){myAnim.update()},this.delay.fade);
},
// and as you would expect the stop() stops it.
stop : function () {
if (!this.interval) return; // do nothing if the animation had already stopped
clearInterval(this.interval);
this.interval = null;
}
}
// this is the jQuery hook in order to run the animation the moment the document is ready
$(document).ready(
function(){
// the first two parameters are the two classnames of the elements
// the last two parameters are the delay between the animation repeating and the time taken for each animation (fade) to happen. The first one should always be bigger
myAnim.init("admin-stats-big-figures","admin-stats-big-figures-hidden",500,200);
}
);
OK, so now we need the HTML to compliment this (as I say I have added a little formatting):
<div><span class="admin-stats-big-figures">One</span> </div>
<div><span class="admin-stats-big-figures-hidden">Two</span> </div>
<hr/>
<input type="button" value="Start" onclick="myAnim.start()"/> | <input type="button" value="Stop" onclick="myAnim.stop()"/>
I have also provided buttons to stop/start the animation. You can see a working example at this JSFiddle - although the stop/start buttons are not working (presumably something specific to JSFiddle) they do work when in context though.
Here im gonna just replace your $(this). and maybe it'll work then.. also using callback.
function ChangeStats() {
$('body').find('.admin-stats-big-figures-hidden').fadeIn(500, function() {
$('body').find('.admin-stats-big-figures').fadeOut(500);
});
}
$(document).ready(function(){
setTimeout('ChangeStats()', 5000);
});
I have a function that add a class (a animation) on the element, but it is not working as planned.
I want that the user add one class at a time. How verify this? I really don't know how to do that with raw Javascript. With jQuery would be easy, I think, because jQuery has the brilliant one() function.
I try to remove the class when the animation stops, but the user still can add multiple classes. And so, there will be several classes during the animation and will be removed only when the animations are gone. Look:
el.classList.add("test", anotherClass);
el.addEventListener("webkitAnimationEnd", function() {
el.classList.remove(anotherClass);
}, true);`
Basically, what im trying to do:
Button click > Add class > When finished or interrupted by another button (and, therefore, new class), remove class or add the new
Obs.: I'm noob with JavaScript, so, please, do not explain with difficult words.
Try something simple:
function setSingleClass(el, singleClass)
{
if (!el._isSingleClassSet) {
el.classList.add(singleClass);
el._isSingleClassSet = 1
el.addEventListener("webkitAnimationEnd", function () {
el.classList.remove(singleClass);
el._isSingleClassSet = 0
}, true);
}
}
I'm making a very simple pop up where I can choose from 8 types of content, all using the same format. It works by clicking on hidden divs that show on hover on the top section, of course as you can see I came up with a very long and large code for something that can probably be done with much less bolts and wires.
Since it's a lot of lines I pasted all this in jsFiddle
Is there a way to make this lighter?
SOLVED... Yeees!
Thanks to all... here is the final script: jsFiddle Final in case someone else has the same difficulty
I see three things you could do to end up with less code:
Use the hover shortcut
Use anonymous functions
Use a parameterized function
First, jQuery has the hover method to replace mouseover/mouseout behavior:
$('#scrollbtnR8').bind('mouseover', R8);
$('#scrollbtnR8').bind('mouseout', R8b);
Can be replaced as
$('#scrollbtnR8').hover(R8, R8b);
That's just a little less code though :)
Second, you are now defining a function for each eventhandler but you are only using these functions once. If you only use them once, you could create anonymous functions like this:
$('#scrollbtnR1').bind('click', esta1);
becomes
$('#scrollbtnR1').click(function() {
$("#scrollcontrol").animate({"left":-240},500, "swing", null);
});
Finally, you can use a function to encapsulate the common parts of the code and pass the changing parts as parameters.
You could implement it like this:
<div id="scrollcontrol" animate="swing" swingLeft="-240">
$('div[animate='swing']').click(function() {
$(this).animate({"left": $(this).attr("swingLeft")}, 500, "swing", null);
});
The "div" selector uses the Attribute Equals Selector. You could also assign a class to all "animate" divs and select them using the class selector: $("div.animate") which would select all <div class="animate">.
What happens here is:
Select all divs that have the value 'swing' for the animate attribute.
Call jQuery swing animate BUT use $(this).attr("swingLeft") as the left property.
attr("wingLeft") gets the value for the swingLeft property as defined in your HTML markup.
I stopped looking at your code at this point, the key is: Look at the code to be executed and see if you notice a pattern, something that you can generalize. You could then perhaps simplify the code further.
Also worth noting: Some people don't like adding properties like "animate", "swingLeft" etc to the HTML. Update As per pimvdb's comment, you can use the jQuery data to be a bit 'cleaner'.
The code could look like this then:
<div id="scrollcontrol" class="swing" data-swingLeft="-240">
$('.swing').click(function() {
$(this).animate({"left": $(this).data("swingLeft")}, 500, "swing", null);
});
Refactor out all of the repeated behavior into a function and then pass the different pieces in as parameters, e.g. this:
function esta1(event) {
$("#scrollcontrol").animate({"left":-240},500, "swing", null);
}
function esta2(event) {
$("#scrollcontrol").animate({"left":0},500, "swing", null);
}
...
$('#scrollbtnR1').bind('click', esta1);
$('#scrollbtnL2').bind('click', esta2);
Becomes this:
function functionName(event, left) {
$("#scrollcontrol").animate({"left": left},500, "swing", null);
}
$('#scrollbtnR1').bind('click', function (e) {
functionName(e, -240);
});
$('#scrollbtnL2').bind('click', function (e) {
functionName(e, 0);
});
Continue this refactoring process until you're left with a single generic function, and everything else passes parameters to that function.
If you find your parameter list getting long and unweildy, consider passing a parameter object instead:
function functionName(event, params) {
$("#scrollcontrol").animate({"left": params.left},
params.duration,
params.animation, null);
}
$('#scrollbtnR1').bind('click', function (e) {
functionName(e, { left: -240, duration: 500, animation: "swing" });
});
For a start, stop referencing everything with IDs. Use classes, like this:
$('.scrollbtn').hover(function(){
$(this).css("opacity","0")
.animate({"opacity":1},500, "linear");
}, function() {
$(this).css("opacity","1")
.animate({"opacity":0},500, "linear");
});
We're trying to make sure our JavaScript menu, which loads content, doesn't get overrun with commands before the content in question loads and is unfurled via .show('blind', 500), because then the animations run many times over, and it doesn't look so great. So I've got about six selectors that look like this:
("#center_content:not(:animated)")
And it doesn't seem to be having any effect. Trying only :animated has the expected effect (it never works, because it doesn't start animated), and trying :not(div) also has this effect (because #center_content is a div). For some reason, :not(:animated) seems not to be changing the results, because even when I trigger the selector while the div in question is visibly animated, the code runs. I know I've had success with this sort of thing before, but the difference here eludes me.
$("#center_content:not(:animated)").hide("blind", 500, function () {
var selector_str = 'button[value="' + url + '"]';
//alert(selector_str);
var button = $(selector_str);
//inspectProperties(button);
$("#center_content:not(:animated)").load(url, CenterContentCallback);
if (button) {
$("#navigation .active").removeClass("active");
button.addClass("active");
LoadSubNav(button);
}
});
I hope this provides sufficient context. I feel like the second selector is overkill (since it would only be run if the first selector succeeded), but I don't see how that would cause it to behave in this way.
Here's the snippet that seemed to be working in the other context:
function clearMenus(callback) {
$('[id$="_wrapper"]:visible:not(:animated)').hide("blind", 500, function() {
$('[id^="edit_"]:visible:not(:animated)').hide("slide", 200, function() {
callback();
});
});
}
Here, the animations queue instead of interrupt each other, but it occurs to me that the selector still doesn't seem to be working - the animations and associated loading events shouldn't be running at all, because the selectors should fail. While the queueing is nice behavior for animations to display, it made me realize that I seem to have never gotten this selector to work. Am I missing something?
Sometimes it's helpful to use .stop() and stop the current animation before you start the new animation.
$("#center_content").stop().hide("blind", 500, function () {});
Really depends on how it behaves within your environment. Remember that .stop() will stop the animation as it was (eg. halfway through hiding or fading)
I don't know if I understand it correctly, but if you want to make sure the user doesn't trigger the menu animation again while it's currently animating(causing it to queue animations and look retarded, this works and should help. I use an if-statement. And before any mouseover/off animation I add .stop(false, true).
$('whatever').click(function(){
//if center_content is not currently animated, do this:
if ($("#center_content").not(":animated")) {
$(this).hide(etc. etc. etc.)
}
//else if center_content IS currently animated, do nothing.
else {
return false;}
});
another example i found elsewhere:
if($("#someElement").is(":animated")) {
...
}
if($("#someElement:animated").length) {
...
}
// etc
then you can do:
$("#showBtn").attr("disabled", $("#someElement").is(":animated"));
I've built a webpage that's supposed to increase the size of images onmouseover.
I'm not replacing the images with bigger ones but rather "stretch" the existing ones because of system limitations.
Here's the webpage:
http://www.catmoviez.com/IMDBQueries.aspx
You can see that the movie images get bigger when you're on them.
Problem is when I move my mouse too quick that sometimes an image gets stuck open or it causes inifinite flickering.
attached is also the code I'm using for the resize:
function resizeImage(elem,width,height){
var myEffect = new Fx.Morph(elem, {duration: 350});
myEffect.start({'height': height,'width': width});
}
First thing, set this variable outside your functions
var imagegrow
And then mouseover this
function () {
imagegrow = setTimeout(function(){ resizeImage(elem,width,height); },1000);
}
And the mouseout this:
function () {
clearTimeout(imagegrow);
}
Adjust the 1000 number to suit your preferred delay (it's in milliseconds). I'd write the whole code for you, but I haven't used MooTools for a while.
Comment if you have any questions
Faruz, Gaussie is right you need to use a timeout. However, consider using mootools' addEvent function as described in the mootools docs as well as the $$ function which will allow you to achieve something much more elegant, along the lines of:
window.addEvent('domready', function() {
$$("tr td input").addEvent("mouseover", function() {
//anonymous function like Gaussie's here
});
});
Note that this isn't the exact code, it will take some modification but it is cleaner and should be more efficient then setting the onmouseover property of every image. Also, remember this goes in the head of your HTML document.