Can I optimize this jQuery 'toggle' image source function? - javascript

I have this functionality witch i am not sure is the best. It is actually working. I'm asking because i couldn't find any 'copy-paste' solution over the net so i wrote this. There no need to suggest other CSS solutions, i am stuck with the <a href><img>text</a> structure and i am UNABLE to write .css (all this because of back-end coding restrictions/ ' they are overwhelmed : lol ' )
javascript (an easy way to let the client build his own icon set [stuck with .png]) :
$(".list-habiliy").on({
mouseenter: function(){
$('img.icone', this).attr("src",$('img.icone', this).attr("src").replace('.png', '-o.png'));
},
mouseleave: function(){
$('img.icone', this).attr("src",$('img.icone', this).attr("src").replace('-o.png', '.png'));
}
},"a");
html (the list of <a> can come up to 30 elements) :
<div class="list-habiliy">
<img class="icone" src="/path/to/default/icons/icon-24px-icon-name1.png" alt="" width="24" height="24" />Some text1
<img class="icone" src="/path/to/default/icons/icon-24px-icon-name2.png" alt="" width="24" height="24" />Some tex2t
<img class="icone" src="/path/to/default/icons/icon-24px-icon-name3.png" alt="" width="24" height="24" />Some text3
<img class="icone" src="/path/to/default/icons/icon-24px-icon-name4.png" alt="" width="24" height="24" />Some text4
</div>
The goal of the function is to replace the icon <img> from within an <a> by adding/removing '-o' text from image source. I'm wondering, should i use the .each(), the hover() for performance reason ?
jsFiddle :
http://jsfiddle.net/5dpaA/
Is this the best way to do it ?
Thanks for all your advices.
[Finaly]:
Explained by user #Xotic750 [accepted answer] (Instead of wrapping this in jquery we use the event attribute and directly access the elements using javascript, we also don't perform any further jquery searches..)
This was somehow the only optimisation i could make.
thanks to user #codelio [i can't accept 2 answers] for is shortened code writing :
$(".list-habiliy a").on({
mouseenter: function (e) {
var elm=e.delegateTarget.firstChild;
elm.src=elm.src.replace('.png','-o.png');
},
mouseleave: function (e) {
var elm=e.delegateTarget.firstChild;
elm.src=elm.src.replace('-o.png','.png');
}
});

Here is another solution, uses jquery event delegation, so only 1 event handler (well 2, one for mounseenter and one for mouseleave) attached to list-habiliy, if you had multiple such structures then you could attach it to document,body and change the selectors to list-habiliy a,img. Instead of wrapping this in jquery we use the event attribute and directly access the elements using javascript, we also don't perform any further jquery searches as we are now assuming that your html pattern does not deviate from that which you have stated. Still, would be pretty difficult to measure it's improvement as it is a user fired event, but it should be faster than your jquery only methods.
HTML
<div class="list-habiliy">
<img class="icone" src="http://placehold.it/64x64/&text=.png1" alt="" width="64" height="64" />Some text1
<img class="icone" src="http://placehold.it/64x64/&text=.png2" alt="" width="64" height="64" />Some tex2t
<img class="icone" src="http://placehold.it/64x64/&text=.png3" alt="" width="64" height="64" />Some text3
<img class="icone" src="http://placehold.it/64x64/&text=.png4" alt="" width="64" height="64" />Some text4
</div>
Javascript
$(".list-habiliy").on("mouseenter", "a,img", function (evt) {
var target = evt.target;
if (target.nodeName === "IMG") {
target.src = target.src.replace('.png', '-o.png');
} else {
target.firstChild.src = target.firstChild.src.replace('.png', '-o.png');
}
}).on("mouseleave", "a,img", function (evt) {
var target = evt.target;
if (target.nodeName === "IMG") {
target.src = target.src.replace('-o.png', '.png');
} else {
target.firstChild.src = target.firstChild.src.replace('-o.png', '.png');
}
})
On jsfiddle

this will allways find the hovered child which is your img, and it's fast!
$(".list-habiliy a").on({
mouseenter: function (e) {
//faster is not possible!
var elm=e.delegateTarget.firstChild, src=elm.src.replace('.png','-o.png');
elm.src=src;
},
mouseleave: function (e) {
//same a bit jQuery stylish
var elm=e.delegateTarget.firstChild, src=elm.src;
$(elm).attr('src',src.replace('-o.png','.png'));
}
});
sorry there is a shorter one. :)
$(".list-habiliy a").on({
mouseenter: function (e) {
var elm=e.delegateTarget.firstChild;
elm.src=elm.src.replace('.png','-o.png');
},
mouseleave: function (e) {
var elm=e.delegateTarget.firstChild;
elm.src=elm.src.replace('-o.png','.png');
}
});

The way you have it works perfectly, and I couldn't say using .hover() will have any performance benefits and .each() is unnecessary. In fact:
Calling $(selector).hover(handlerInOut) is shorthand for:
$(selector).on("mouseenter mouseleave", handlerInOut);

I would probably store the $('img.icone', this) query in a variable so that it's not doing two queries inside each of the mouseenter / mouseleave functions.
It also increases readability and reduces potential problems should this be cut/pasted onto other areas:
$(".list-habiliy").on({
mouseenter: function () {
var imgIcon = $('img.icone', this);
imgIcon.attr("src", imgIcon.attr("src").replace('.png', '-o.png'));
},
mouseleave: function () {
var imgIcon = $('img.icone', this);
imgIcon.attr("src", imgIcon.attr("src").replace('-o.png', '.png'));
}
}, "a");
JS Fiddle demo: http://jsfiddle.net/5dpaA/3/

Hover is implemented like this (as seen here):
hover: function( fnOver, fnOut ) {
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
}
So no performance here. Avoiding delegation would create a lot of handlers instead.
What you can actually do is add something like this at the end of your html:
<div id="hidden-img">
<img class="icone" src="http://placehold.it/64x64/&text=-o.png1" alt="" width="64" height="64" />
<img class="icone" src="http://placehold.it/64x64/&text=-o.png2" alt="" width="64" height="64" />
<img class="icone" src="http://placehold.it/64x64/&text=-o.png3" alt="" width="64" height="64" />
<img class="icone" src="http://placehold.it/64x64/&text=-o.png4" alt="" width="64" height="64" />
</div>
And add some CSS:
#hidden-img {
margin-left: -9999px;
}
I think it's Opera that won't load images if they're hidden.

Related

div blinking on using jquery .hover()

<script>
$(document).ready(function () {
$("#logo_").hide();
$("#logo_learn").hide();
})
function sl() {
$("#logo_learn").slideToggle(500);
$("#logo_").fadeIn();
}
function hl() {
$("#logo_learn").slideToggle(500);
$("#logo_").fadeOut();
}
</script>
<img id="logo_learn" src="img/logo_learn.png"></img>
<img id="logo" src="img/logo.png" onmouseover="sl()" onmouseout="hl()" ></img>
<img id="logo_" src="img/logo_.png" ></img>
</html>
I have this html such that on hovering on logo.png, logo_ and logo_learn are shown but when i run this on hovering on logo.png, logo_ $ logo_learn blinks.pls post a simple answer.
Use onmouseenter and .stop() like this
HTML
<img id="logo_learn" src="img/logo_learn.png"></img>
<img id="logo" src="img/logo.png" onmouseenter="sl()" onmouseleave="hl()" ></img>
<img id="logo_" src="img/logo_.png" ></img>
jQuery
function sl() {
$("#logo_learn").stop().slideToggle(500);
$("#logo_").stop().fadeIn();
}
function hl() {
$("#logo_learn").stop().slideToggle(500);
$("#logo_").stop().fadeOut();
}
DEMO
Update
What #epascarello said, dont use inline event handlers, use .on() like this code below
$('#logo').on('mouseenter', function () {
$("#logo_learn").stop().slideToggle(500);
$("#logo_").stop().fadeIn();
});
$('#logo').on('mouseleave', function () {
$("#logo_learn").stop().slideToggle(500);
$("#logo_").stop().fadeOut();
});

bounce animation using constructor?

I want to give a bounce animation to each individual hair on mouseover.the animation part is done on the first hair,but i don`t want to give events to each and every hair image.
So here is my question,I am using constructor in my javascript code.Is it possible that I create a method in my prototype and make instances of it? So basiclly I dont want to fire 10 events or 10 addEventListeners,I want a smart way out of this.
How do I complete my task,as in every hair should bounce on mouseover of itself only
My code:
HTML:
<div class="main">
<div class="hair">
<img src="images/single.png" id="hair1" width="13" height="40" >
<img src="images/single.png" id="hair2" width="13" height="40">
<img src="images/single.png" id="hair3" width="13" height="40">
<img src="images/single.png" id="hair4" width="13" height="40">
<img src="images/single.png" id="hair5" width="13" height="40">
<img src="images/single.png" id="hair6" width="13" height="40">
<img src="images/single.png" id="hair7" width="13" height="40">
<img src="images/single.png" id="hair8" width="13" height="40">
<img src="images/single.png" id="hair9" width="13" height="40">
</div>
<div class="face">
<img src="images/ec_logo.png">
</div>
Javascript:
(function(){
hair=function (){
return this;
};
hair.prototype={
bounce:function(){
//code for some bounce animation
}
};
})();
document.getElementById('hair??').addEventListener("mouseover",???,false);????????//this is the part i am confused about,how to give same animation to every single hair by using object instances???
// one event listener for whole 'haircut'
document.getElementsByClassName('hair')[0].addEventListener('mouseover',
function(objEvent)
{
var elImg = objEvent.target;
if (elImg.nodeName == 'IMG')
{
objEvent.stopPropagation();
console.log(elImg.getAttribute('id')); // just for debug
// xxx.bounce(elImg) or whatever you want
}
}, false);
You can delay the bounce using setTimeout:
(function(){
// "private" vars
var delayPace = 100;// ms
var instanceCount = 0;
hair = function (){
// don't need to return this if it's going
// to be instantiated via the new keyword
this.delay = instanceCount * delayPace;
instanceCount++;
};
hair.prototype={
delay : 0,
bounce : function(){
this.stop();
this._t = setTimeout(this._bounce, this.delay);
},
_bounce : function(){
// this is were the actual magic happens
},
stop : function(){
if(this.t){
clearTimeout(this.t);
}
// other code to stop the animation
}
};
})();
// keep all your hairs in one place!
var hairs = [];
// instantiate them
hairs.push(new hair());
//...
hairs.push(new hair());
var onHover = function(){
hairs.forEach(function(h){
h.bounce();
});
}

Access all items under specific div in javascript

Hi i want to access all images under some specific div in javascript.My code is :
<div id="image-container" style="background-image:url(loading.gif)">
<div class="fade-box" id="image-1"><img src="2bscene-logo.gif" alt="" width="330" height="108" border="0" /></div>
<div class="fade-box" id="image-2" style="display: none;"><img src="streetgallery-logo.gif" alt="" width="330" height="108" border="0" /></div>
<div class="fade-box" id="image-3" style="display: none;"><img src="g4m-logo.gif" alt="" width="330" height="108" border="0" /></div>
</div>
While my js code is :
var image_slide = new Array('image-1', 'image-2', 'image-3');
I want to get all DIVs based on id dynamically, not having a predefined array. How can I do that?
By pure javascript, you can try:
var arr = document.querySelectorAll('#image-container img');
for(var i=0;i<arr.length;i++){
console.log(arr[i].getAttribute('src'))
}
On browsers that support querySelectorAll. (IE8 and up, modern versions of others, not IE7 and down.) - As mentioned in the comment by T.J. Crowder
With JavaScript, you can do this easily with the DOM:
var container = document.getElementById("image-container");
var child;
var image_slide = [];
for (child = container.firstChild; child; child = child.nextSibling) {
if (child.id && child.nodeName === "DIV") {
image_slide.push(child.id);
}
}
Live Example | Source
Note that the code above must be run after the elements are already in the DOM. The best way to ensure that is to put the script tag below them in the page (just before the closing </body> element is good).

Pause and resume jquery animation?

Here's a fiddle:
http://jsfiddle.net/vBt5E/
I want to pause and unpause an endlessly scrolling jquery animation on hover, without any weird jumps.
html like this:
<div id="vertical-carousel">
<div class="imagecolumn">
<img src="http://placekitten.com/200/120" width="200" height="120">
<img src="http://placekitten.com/200/100" width="200" height="100">
<img src="http://placekitten.com/200/80" width="200" height="90">
</div>
</div>
​
javascript like so:
var imageColumn = $('.imagecolumn');
origColumnHeight = imageColumn.height();
$(document).ready(function() {
var columnDupe = imageColumn.contents()
.clone()
.addClass('dupe')
.appendTo(imageColumn);
function scrollColumn() {
imageColumn.css({'top': '0'})
.animate({top: -origColumnHeight},15000, 'linear', scrollColumn);
}
scrollColumn();
});
I know this has been asked in different form before but the various answers aren't working for me. I looked at Tobia Conferto's "Pause" plugin and just couldn't get it to work. Ditto this one, which is supposedly even buggier.
Please post a working fiddle if you can, it really helps. Thanks!
Using the "Pause" plugin, you can get it working by doing the following:
$(".imagecolumn").hover(function() {
$(this).pause();
}, function() {
$(this).resume();
});
Example: http://jsfiddle.net/charlescarver/vBt5E/1/
Make sure you include the plugin on your page! https://raw.github.com/tobia/Pause/master/jquery.pause.min.js
P.S. I prefer PlaceDog.

How to use dynamic div ID

I've been looking at so many answers here that are so close but because my jQuery knowledge is so patchy, I can't make them relevant. So I'll just go ahead and ask. I hope you all don't mind!
So, here's the script:
$("#button1").mouseenter(function(){
$("#a1").fadeIn('100', function() { });
$("#button1").mouseleave(function(){
$("#a1").fadeOut('100', function() {});
});
});
I have multiple buttons which, on mouseenter, I want to activate the corresponding arrow.
Rather than repeat the script for each pair - #button2 to #arrow2 etc - how can I just put in some neat variable in the $() bit and have it work? Sounds simple, I'm sure there's a way that my denseness cannot find.
This is the HTML (for those who requested it):
<div id="buttons">
<p><img src="images/1.png" name="button1" id="button1" /></p>
<p><img src="images/2.png" name="button2" id="button2" /></p>
<p><img src="images/3.png" name="button3" id="button3" /></p>
<p><img src="images/4.png" name="button4" id="button4" /></p>
<p><img src="images/5.png" name="button5" id="button5" /></p>
</div>
<div class="arrow" id="a1"><img src="images/arrow.png" width="747" height="75" /></div>
<div class="arrow" id="a2"><img src="images/arrow.png" width="747" height="75" /></div>
<div class="arrow" id="a3"><img src="images/arrow.png" width="747" height="75" /></div>
<div class="arrow" id="a4"><img src="images/arrow.png" width="747" height="75" /></div>
<div class="arrow" id="a5"><img src="images/arrow.png" width="747" height="75" /></div>
<div class="arrow" id="a6"><img src="images/arrow.png" width="747" height="75" /></div>
Your solution doesn't scale.
You are assigning eventhandlers for each element in the dom.
This is one of the beefs I have with jQuery. It makes it very easy to do these things wrong and shoot yourself in the foot along the way.
What you need is event delegation.
Essentially the concept is that you have an event handler much higher up in the dom tree that listens to events that bubble up. So you might be handling your mouse events on a list, not on the list items themselves, but on the document, in a single event handler.
Take a look at http://api.jquery.com/on/
Your code should be something like this:
$('body').on('mouseenter', '#buttons img', function (e) {
$('#a' + $(this).attr('id').slice(-1)).fadeIn(300);
});
$('body').on('mouseleave', '#buttons img', function (e) {
$('#a' + $(this).attr('id').slice(-1)).fadeOut(300);
});
Notice that I'm actually only using the id to get a link between the buttons and the arrows. You might consider skipping the id's all together and just go by what index the element has in its parent element.
Working example can be found here: http://jsfiddle.net/GNs44/1/
$(".buttonClass").mouseenter(function(){
$(this).siblings('.aClass').fadeIn('100', function() { });
$(".buttonClass").mouseleave(function(){
$(this).siblings('.aClass').fadeOut('100', function() {});
});
});
You can use this (or similar, instead of siblings use a proper selector) and add the mentioned classes (aClass and buttonClass) to your components).
An example where this works:
<div>
<button class="buttonClass">Description</button>
<a class="aClass">Description</a>
</div>
Note that it's important to "group" them inside a div tag, because otherwiese ALL <a> tags will fade in/out when you hover a button.
Rather than using the ID, give the button a class.
HTML:
<button id="button1" class="hoverbutton" />
<button id="button2" class="hoverbutton" />
jQuery:
$(".hoverbutton").mouseenter(function () {
$(".arrowclass").fadeIn('100', function () { });
});
$(".hoverbutton").mouseleave(function () {
$(".arrowclass").fadeOut('100', function () { });
});
Something like this might get you started:
$("[id*=button]").mouseenter(function(){
var t = $(this),
idnum = t.slice(-1);
$("[id=a" + idnum + "]").fadeIn('100', function() {
});
}).mouseleave(function(){
var t = $(this),
idnum = t.slice(-1);
$("[id=a" + idnum + "]").fadeOut('100', function() {
});
});

Categories

Resources