JavaScript/jQuery onmouseover problem - javascript

I want that when mouse is over an image, an event should be triggered ONCE, and it should be triggered again only after mouse is out of that image and back again, and also at least 2 seconds passed.
My current function is called continuously (refreshcash) if I leave the mouse over my image
<img src="images/reficon.png" onmouseover="refreshcash()" onmouseout="normalimg()" id="cashrefresh"/>
function refreshcash() {
$("#showname").load('./includes/do_name.inc.php');
$("#cashrefresh").attr("src","images/reficonani.gif");
}
function normalimg() {
$("#cashrefresh").attr("src","images/reficon.png");
}
code update
This code seems to have a bug,but the algorithm is kinda logical
<script type="text/javascript">
var canhover = 1;
var timeok = 1;
function redotimeok() {
timeok = 1;
}
//
function onmenter()
{
if (canhover == 1 && timeok == 1)
{
enter();
canhover = 0;
}
}
//
function onmleave()
{
leave();
canhover = 1;
setTimeout(redotimeok(), 2000);
leave();
}
//
$('#cashrefresh').hover(onmenter(),onmleave());
function enter(){
$("#showname").load('./includes/do_name.inc.php');
$("#cashrefresh").attr("src","images/reficonani.gif");
}
function leave(){
$("#cashrefresh").attr("src","images/reficon.png");
}
</script>

Try the hover:
$('#cashrefresh').hover(function(){
$("#showname").load('./includes/do_name.inc.php');
$("#cashrefresh").attr("src","images/reficonani.gif");
}, function(){
$("#cashrefresh").attr("src","images/reficon.png");
});
And your image should look like:
<img src="images/reficon.png" id="cashrefresh"/>
Update:
Modify your code like this:
var e = null;
var l = null;
$('#cashrefresh').hover(function(){
e = setTimeout(enter, 2000)
}, function(){
l = setTimeout(leave, 2000)
});
function enter(){
$("#showname").load('./includes/do_name.inc.php');
$("#cashrefresh").attr("src","images/reficonani.gif");
clearTimeout(e);
}
function leave(){
$("#cashrefresh").attr("src","images/reficon.png");
clearTimeout(l);
}

Do you have the images cached in some way? If you replace them by their src attribute without specifying width/height elsewhere (best would be CSS) or having them readily available then the hovered box (img element) will collapse into a smaller (or no) box until the image has been loaded far enough for the browser to know the correct dimensions of the image to resize the box (which may affect other elements being adjusted to the image). The exact effect depends on the browser but you may lose the hover state causing the call of your mouseout function.
I assume that both images are the same size, so if you didn't already, you could try adding the dimensions to your CSS for #cashrefresh and see if that fixes the problem.
For the delay I would recommend using the jQuery timers plugin (or a similar one) which eases handling of timers compared to doing it on your own. You would probably want to give your timers names and try to stop older ones before you add the next one.

Related

onmouseout and onmouseover

I am working on homework that involves working with javascript. Part of my homework assignment is to use the event handlers onmouseout and onmouseouver. What is supposed to happen when the user hovers over a specific div element, the font size grows by 25%, and when the user mouses out of the div element, the font size goes back to normal. My question is, is it possible to incorporate both an onmouseover function and an onmouseout function into one function? Somehow that is what my teacher wants us to do. I have this started so far.
function FontSize(x)
{
x.style.fonstSize = large;
}
I'm also thinking this isnt the correct code to make the font 25% larger, but I'm not sure how to really incorporate an onmouseout in this function.
As a teacher myself, I am 99% sure that by "one function" the instructor means one general-purpose function to change the font size, not one function which uses conditional statements to work backwards and figure out whether it should be doing onmouseout or onmouseover.
Your script should contain:
function resize(elem, percent) { elem.style.fontSize = percent; }
Your HTML should contain:
<div onmouseover="resize(this, '125%')" onmouseout="resize(this, '100%')"
Text within div..
</div>
Note: Situations such as here, are exactly why JavaScript has the keyword "this"--to save us from needing to use complicated document.getElementById() statements.
You can use "%" property for controlling font-size as described here with the following code.
document.getElementById("div1").onmouseover = function() {
document.getElementById("div1").style.fontSize = "125%"
};
document.getElementById("div1").onmouseout = function() {
document.getElementById("div1").style.fontSize = "100%";
};
Here is the working jsfiddle : http://jsfiddle.net/LxhdU/
Yes you can. Call the same function on both events, and pass a parameter to indicate whether the fontsize should increase or decrease.
ChangeFontSize = function(element, shouldIncreaseFontsize)
{
var small=14;
var large = small * 1.25;
if(shouldIncreaseFontsize) {
element.style.fontSize = large + "px";
}
else {
element.style.fontSize = small + "px";
}
}
http://jsfiddle.net/TMHbW/1/
I'd do something simple like the following. The large and small values can be whatever you need them to be for the font size to work or they can be variables you've defined in prior code.
Demo: http://jsfiddle.net/lucuma/EAbYn/
function doHover(e) {
if (e.type=='mouseover') {
this.style.fontSize = "large";
} else {
this.style.fontSize = "small";
}
}
var el = document.getElementById('myelement')
el.onmouseout =doHover;
el.onmouseover=doHover;
It is possible you do not need to call both the events on the element explicitly instead extension you create will do that.Extend the Element's prototype. Jquery also does similar to this.
Ref Prototype
See Fiddle:- http://jsfiddle.net/4fs7V/
Element.prototype.hover= function( fnOver, fnOut ) {
this.onmouseover=fnOver;
this.onmouseout=fnOut || fnOver;
return this;
};
document.getElementById('test').hover(function(){
//do your mouseover stuff
},
function(){
//do your mouseout stuff
});
Update
Same can be achieved with just one function too:-
Hover me
.largeFont {
font-size:125%;
}
Element.prototype.hover = function (fnOver, fnOut) {
this.onmouseover = fnOver;
this.onmouseout = fnOut || fnOver;
return this;
};
document.getElementById('test').hover(changeMe);
function changeMe()
{
if(this.hasAttribute('class'))
{
this.removeAttribute('class');
}
else
{
this.setAttribute('class', 'largeFont');
}
}

Jquery - Carasol build finished and would like advice on best practice / neatening up my code

I have been building my own carasol over the past few days.
My Jquery is based on tutorials on the web and also from help and advice from SO.
I am not a Jquery guru just an enthusiast and think my code is a little sloppy, hence the post.
here is a link to the working code: http://jsfiddle.net/JHqBA/2/ (updated link)
basically what happens is:
if someone hits the page with a # values in the url it will show the appropriate slide and example would be www.hello.com#two, this would slide to slide two
if someone clicks the numbers it will show the appropriate slide
next and prev also slide through the slides.
The question is, is there anything i could have wrote better as i know there is alot of duplicate code.
I understand its a big ask but it would help me learn a little more (i think my code is a little old school)
if anyone has any questions please feel free to ask and ill answer what it does or is supposed to do.
Sluap
--- Edit ----
I have made only one aniamtion function now which has got rid of alot of duplicate code.
I have yet to look into on function but will do soon.
I would like to know more about the create a new function, outside of the jQuery ready block as i cant get this working or quite understand how i can get it to work sorry
any more tips would be great ill carry on working on this project till i am happy with it.
also is there a better way to write:
if ($slideNumber == 1) {
$('#prev').attr("class", "not_active")
$('#next').attr("class", "active")
}
else if ($slideNumber == divSum) {
$('#next').attr("class", "not_active");
$('#prev').attr("class", "active");
}
else {
$('#prev').attr("class", "active")
$('#next').attr("class", "active")
};
Jquery full:
$(document).ready(function () {
//////////////////////////// INITAL SET UP /////////////////////////////////////////////
//Get size of images, how many there are, then determin the size of the image reel.
var divWidth = $(".window").width();
var divSum = $(".slide").size();
var divReelWidth = divWidth * divSum;
//Adjust the image reel to its new size
$(".image_reel").css({ 'width': divReelWidth });
//set the initial not active state
$('#prev').attr("class", "not_active");
//////////////////////////// SLIDER /////////////////////////////////////////////
//Paging + Slider Function
rotate = function () {
var triggerID = $slideNumber - 1; //Get number of times to slide
var image_reelPosition = triggerID * divWidth; //Determines the distance the image reel needs to slide
//sets the active on the next and prev
if ($slideNumber == 1) {
$('#prev').attr("class", "not_active")
$('#next').attr("class", "active")
}
else if ($slideNumber == divSum) {
$('#next').attr("class", "not_active");
$('#prev').attr("class", "active");
}
else {
$('#prev').attr("class", "active")
$('#next').attr("class", "active")
};
//Slider Animation
$(".image_reel").animate({
left: -image_reelPosition
}, 500);
};
//////////////////////////// SLIDER CALLS /////////////////////////////////////////////
//click on numbers
$(".paging a").click(function () {
$active = $(this); //Activate the clicked paging
$slideNumber = $active.attr("rel");
rotate(); //Trigger rotation immediately
return false; //Prevent browser jump to link anchor
});
//click on next button
$('#next').click(function () {
if (!$(".image_reel").is(':animated')) { //prevent clicking if animating
var left_indent = parseInt($('.image_reel').css('left')) - divWidth;
var slideNumberOn = (left_indent / divWidth);
var slideNumber = ((slideNumberOn * -1) + 1);
$slideNumber = slideNumber;
if ($slideNumber <= divSum) { //do not animate if on last slide
rotate(); //Trigger rotation immediately
};
return false; //Prevent browser jump to link anchor
}
});
//click on prev button
$('#prev').click(function () {
if (!$(".image_reel").is(':animated')) { //prevent clicking if animating
var left_indent = parseInt($('.image_reel').css('left')) - divWidth;
var slideNumberOn = (left_indent / divWidth);
var slideNumber = ((slideNumberOn * -1) - 1);
$slideNumber = slideNumber;
if ($slideNumber >= 1) { //do not animate if on first slide
rotate(); //Trigger rotation immediately
};
}
return false; //Prevent browser jump to link anchor
});
//URL eg:www.hello.com#one
var hash = window.location.hash;
var map = {
one: 1,
two: 2,
three: 3,
four: 4
};
var hashValue = map[hash.substring(1)];
//animate if hashValue is not null
if (hashValue != null) {
$slideNumber = hashValue;
rotate(); //Trigger rotation immediately
return false; //Prevent browser jump to link anchor
};
});
Question and answer has been moved over to https://codereview.stackexchange.com/questions/8634/jquery-carasol-build-finished-and-would-like-advice-on-best-practice-neateni/8635#8635
1) Separation of Concerns
Start by refactorring your code in to more granular functions.
You can read more about SoF at http://en.wikipedia.org/wiki/Separation_of_concerns
Update:
E.g. Instead of having your reel resizing code inline, put it in it's own function, like this:
function setImageReelWidth () {
//Get size of images, how many there are, then determin the size of the image reel.
var divWidth = $(".window").width();
var divSum = $(".slide").size();
var divReelWidth = divWidth * divSum;
//Adjust the image reel to its new size
$(".image_reel").css({ 'width': divReelWidth });
}
This achieves 2 things:
a. First, it groups a block of code that is logically cohesive, removing it from the main code which results in a much cleaner code habitat.
b. It effectively gives a label to the code block via the function name that is descriptive of what it does, and therefore makes understanding of the code much simpler.
Later, you can also encapsulate the whole thing in it's own "class" (function) and you can move it into it's own js file.
2) The jQuery "on" function
Use the "on" function to attach your click events, rather than the "click" function.
http://api.jquery.com/on/
This has the added advantage of also binding it to future elements matching your selector, even though they do not exist yet.
3) The ready function
// I like the more succinct:
$(handler)
// Instead of:
$(document).ready(handler)
But you might like the more obvious syntax.
Those are just a few things to start with.
-- Update 1 --
Ok, StackOverflow is not really suited to a refactoring work in progress, but we'll make do. I think you should keep your original code block in your question, so that future readers can see where it started and how it systematically improved.
I would like to know more about the create a new function, outside of
the jQuery ready block as i cant get this working or quite understand
how i can get it to work sorry
I am not familiar with jsfiddle.net, but it looks cool and helpful, but might also be a bit confusing if you don't know what is going on. I am not sure I do :), but I think that script editor window results in a .js file that is automatically referenced by the html file.
So here is an example of a function defined outside of the ready block, but referenced from within.
function testFunction () {
alert ('it works');
}
$(document).ready(function () {
testFunction();
// ... other code
});
This should pop up an alert box that says, "it works" when the page is loaded.
You can try it for yourself.
Then, once you got that working, you can refactor other logically cohesive blocks of code into their own functions. Later you can wrap them all up into their own javascript 'class'. But we'll get to that.

Can I optimise this js Prototype gallery to animate faster in IE?

I'm using a js image gallery on a magento site. Because Magento uses prototype, I've used prototype for this gallery; it's a simple application and I thought it unnecessary to load jQuery library as well just for this one element.
If you have a look at http://web74.justhost.com/~persia28/ in IE8 or less, the transitions between gallery slides is so slow, to the point where the text from one slide remains visible for a short time when the next slide is in place.
I know IE is rubbish with js, but I thought the extent of the slowness here is extreme, even for IE.
I don't want to load jQuery library just for this one gallery, Magento is enough of a tank as it is; so if it came to that I might opt for just putting the text in the images, not in HTML.
Anyway, would be great to hear your wisdom.
Many thanks, and here is the js code for the gallery.
var i = 0;
// The array of div names which will hold the images.
var image_slide = new Array('image-1', 'image-2', 'image-3');
// The number of images in the array.
var NumOfImages = image_slide.length;
// The time to wait before moving to the next image. Set to 3 seconds by default.
var wait = 4000;
// The Fade Function
function SwapImage(x,y) {
$(image_slide[x]).appear({ duration: 1.5 });
$(image_slide[y]).fade({duration: 1.5});
}
// the onload event handler that starts the fading.
function StartSlideShow() {
play = setInterval('Play()',wait);
$('PlayButton').hide();
$('PauseButton').appear({ duration: 0});
}
function Play() {
var imageShow, imageHide;
imageShow = i+1;
imageHide = i;
if (imageShow == NumOfImages) {
SwapImage(0,imageHide);
i = 0;
} else {
SwapImage(imageShow,imageHide);
i++;
}
}
function Stop () {
clearInterval(play);
$('PlayButton').appear({ duration: 0});
$('PauseButton').hide();
}
function GoNext() {
clearInterval(play);
$('PlayButton').appear({ duration: 0});
$('PauseButton').hide();
var imageShow, imageHide;
imageShow = i+1;
imageHide = i;
if (imageShow == NumOfImages) {
SwapImage(0,imageHide);
i = 0;
} else {
SwapImage(imageShow,imageHide);
i++;
}
}
function GoPrevious() {
clearInterval(play);
$('PlayButton').appear({ duration: 0});
$('PauseButton').hide();
var imageShow, imageHide;
imageShow = i-1;
imageHide = i;
if (i == 0) {
SwapImage(NumOfImages-1,imageHide);
i = NumOfImages-1;
} else {
SwapImage(imageShow,imageHide);
i--;
}
}
I looked at the site and it doesn't seem slow, it takes the same time to run. It looks as though the text is not changing in opacity until the end of the animation then is just being hidden. When I look with IE7 it works normally which is a clue, IE8 has a different way of making transparencies.
Magento still ships with Prototype 1.6.0 when I know that Prototype 1.6.1 fixes several IE8 bugs and Prototype 1.7 fixes some IE9 bugs too. You could try upgrading Prototype and Scriptaculous in the js/prototype/ and js/scriptaculous/ directories. I don't know if that exact problem is included which is why you should make a backup before overwriting files, if this doesn't work you will have something to rollback to.

How to create a rotating thumbnails functionality in jQuery?

Basically I need the thumbnails to rotate every time the user hovers over an image. Here is my failed attempt:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
$('img').hover(function() {
theImage = $(this).attr('id');
otherImages = $(this).attr('class').split('#');
rotateThumbs(otherImages, theImage);
}, function() {
//
});
});
function rotateThumbs(otherImages, theImage) {
for (i=0; i < otherImages.length; i++) {
setInterval($('#'+theImage).attr('src', otherImages[i]), 1000);
}
}
</script>
<img id="myImage" src="http://www.google.com/images/logos/ps_logo2.png" class="http://www.google.com/images/logos/ps_logo2.png#http://l.yimg.com/a/i/ww/met/yahoo_logo_us_061509.png#http://dogandcat.com/images/cat2.jpg" width="174" height="130" />
Does anyone know how this may be accomplished?
Some issues here.
setInterval requires a function reference as it's first argument, but you are executing code that returns a jQuery object.
setInterval executes the first function repeatedly at the specified interval. Is that what you are trying to do? Swap images every second?
Depending on how you correct the first issue, you could run into an issue where i is otherImages.length and thus the src is set to undefined.
Assuming you worked around issue 3, you will have the problem that the image swaps will happen imperceptibly fast and it will appear as though the last image is always displayed.
Instead, don't use a loop. Increment a counter each time a new image is displayed. Try this:
function rotateThumbs(otherImages, theImage) {
var i = 0;
var interval = setInterval(function() {
$('#'+theImage).attr('src', otherImages[i++]);
if (i >= otherImages.length) {
clearInterval(interval);
}
}, 1000);
}
I've implemented a fully-functional example here. This addresses some of the issues that #gilly3 notes, but uses closures instead of an incrementing counter to keep track of the current image:
$(function() {
$('img').hover(function() {
// declaring these variables here will preserve
// the references in the function called by setInterval
var $img = $(this),
imageList = $img.attr('class').split('#'),
intervalId;
// start the cycle
intervalId = window.setInterval(function() {
var next = imageList.pop();
if (next) {
$img.attr('src', next);
} else {
// stop the cycle
intervalId = window.clearInterval(intervalId);
}
}, 1000);
}, function() {});
});
As you can see, using a closure is much easier when you declare the function passed to setInterval inline, rather than as a separate, named function. You could still have a rotateThumbs function if you wanted, but you might need to do some more work to ensure that the variables were being passed properly.
Edit: I made an updated version that continues to cycle as long as the mouse is hovering.
I have adjusted the answer for Sam, taking pre-loading the image into account, so that you won't have a possible deplay at the first rotation.
function rotateThumbs(otherImages, theImage) {
if(!$('#'+theImage).data('setup')){
$('#'+theImage).data('setup', true); // do not double pre-loading images
var $body = $('body');
for(var j = 0; j < otherImages.length; j++){
$body.append($('<img/>').attr('src', otherImages[j])
.css('display', 'none'));
}
}
var i= 0;
setInterval(function(){
$('#'+theImage).attr('src', otherImages[i++]);
if(i >= otherImages.length){i = 0}
}, 1000);
}

How to improve image cross-fade performance?

I want to be able to do a cross fade transition on large images whose width is set to 100% of the screen. I have a working example of what I want to accomplish. However, when I test it out on various browsers and various computers I don't get a buttery-smooth transition everywhere.
See demo on jsFiddle: http://jsfiddle.net/vrD2C/
See on Amazon S3: http://imagefader.s3.amazonaws.com/index.htm
I want to know how to improve the performance. Here's the function that actually does the image swap:
function swapImage(oldImg, newImg) {
newImg.css({
"display": "block",
"z-index": 2,
"opacity": 0
})
.removeClass("shadow")
.animate({ "opacity": 1 }, 500, function () {
if (oldImg) {
oldImg.hide();
}
newImg.addClass("shadow").css("z-index", 1);
});
}
Is using jQuery animate() to change the opacity a bad way to go?
You might want to look into CSS3 Transitions, as the browser might be able to optimize that better than Javascript directly setting the attributes in a loop. This seems to be a pretty good start for it:
http://robertnyman.com/2010/04/27/using-css3-transitions-to-create-rich-effects/
I'm not sure if this will help optimize your performance as I am currently using IE9 on an amped up machine and even if I put the browser into IE7 or 8 document mode, the JavaScript doesn't falter with your current code. However, you might consider making the following optimizations to the code.
Unclutter the contents of the main photo stage by placing all your photos in a hidden container you could give an id of "queue" or something similar, making the DOM do the work of storing and ordering the images you are not currently displaying for you. This will also leave the browser only working with two visible images at any given time, giving it less to consider as far as stacking context, positioning, and so on.
Rewrite the code to use an event trigger and bind the fade-in handling to the event, calling the first image in the queue's event once the current transition is complete. I find this method is more well-behaved for cycling animation than some timeout-managed scripts. An example of how to do this follows:
// Bind a custom event to each image called "transition"
$("#queue img").bind("transition", function() {
$(this)
// Hide the image
.hide()
// Move it to the visible stage
.appendTo("#photos")
// Delay the upcoming animation by the desired value
.delay(2500)
// Slowly fade the image in
.fadeIn("slow", function() {
// Animation callback
$(this)
// Add a shadow class to this image
.addClass("shadow")
// Select the replaced image
.siblings("img")
// Remove its shadow class
.removeClass("shadow")
// Move it to the back of the image queue container
.appendTo("#queue");
// Trigger the transition event on the next image in the queue
$("#queue img:first").trigger("transition");
});
}).first().addClass("shadow").trigger("transition"); // Fire the initial event
Try this working demo in your problem browsers and let me know if the performance is still poor.
I had the same problem too. I just preloaded my images and the transitions became smooth again.
The point is that IE is not W3C compliant, but +1 with ctcherry as using css is the most efficient way for smooth transitions.
Then there are the javascript coded solutions, either using js straight (but need some efforts are needed to comply with W3C Vs browsers), or using libs like JQuery or Mootools.
Here is a good javascript coded example (See demo online) compliant to your needs :
var Fondu = function(classe_img){
this.classe_img = classe_img;
this.courant = 0;
this.coeff = 100;
this.collection = this.getImages();
this.collection[0].style.zIndex = 100;
this.total = this.collection.length - 1;
this.encours = false;
}
Fondu.prototype.getImages = function(){
var tmp = [];
if(document.getElementsByClassName){
tmp = document.getElementsByClassName(this.classe_img);
}
else{
var i=0;
while(document.getElementsByTagName('*')[i]){
if(document.getElementsByTagName('*')[i].className.indexOf(this.classe_img) > -1){
tmp.push(document.getElementsByTagName('*')[i]);
}
i++;
}
}
var j=tmp.length;
while(j--){
if(tmp[j].filters){
tmp[j].style.width = tmp[j].style.width || tmp[j].offsetWidth+'px';
tmp[j].style.filter = 'alpha(opacity=100)';
tmp[j].opaque = tmp[j].filters[0];
this.coeff = 1;
}
else{
tmp[j].opaque = tmp[j].style;
}
}
return tmp;
}
Fondu.prototype.change = function(sens){
if(this.encours){
return false;
}
var prevObj = this.collection[this.courant];
this.encours = true;
if(sens){
this.courant++;
if(this.courant>this.total){
this.courant = 0;
}
}
else{
this.courant--;
if(this.courant<0){
this.courant = this.total;
}
}
var nextObj = this.collection[this.courant];
nextObj.style.zIndex = 50;
var tmpOp = 100;
var that = this;
var timer = setInterval(function(){
if(tmpOp<0){
clearInterval(timer);
timer = null;
prevObj.opaque.opacity = 0;
nextObj.style.zIndex = 100;
prevObj.style.zIndex = 0;
prevObj.opaque.opacity = 100 / that.coeff;
that.encours = false;
}
else{
prevObj.opaque.opacity = tmpOp / that.coeff;
tmpOp -= 5;
}
}, 25);
}

Categories