I have a requirement to change the text on hover of multiple menu items at once but I cannot use CSS and nor can I give each individual item its own CSS class. What I would like to do is when the mouse hovers anywhere over the .menu-wrapper the Javascript replaces each of the <li> item texts with the relevant replacement text.
I have a script which works perfectly for a single item:
<div class="menu-wrapper">
<ul>
<li>WORD1</li>
</ul>
</div>
Javascript:
var originalText = $('.menu-wrapper > ul > li').text();
$('.menu-wrapper').hover(function () {
var $p = $(this).find('li');
$p.fadeOut(300, function () {
$(this).text('replacement word 1').fadeIn(300);
});
}, function () {
// set original text
$(this).find('li').fadeOut(300, function () {
$(this).text(originalText).fadeIn(300);
});
});
But obviously if you add multiple <li> items it breaks because it is only storing a single .text() variable and concatenates all the entries after the first mouseout event.
I tried using a switch statement to look for the value of the .text() and change the text value accordingly but it didn't work (my Javascript is weak...).
I'd appreciate any help with this. I only have four items to replace the text of so repeating any script as necessary is not a problem. Normally I would give each one it's own class identity and use what I already have but unfortunately I can't.
Please don't suggest using CSS as I already know how to do that but for this I need to use Javascript.
I could not find this question elsewhere.
Thanks!
Main issue is first line:
var originalText = $('.menu-wrapper > ul > li').text();
This will get all text from all elements in the collection:
What you could do is store that text on each element using jQuery data() by looping over the elements and dealing with instances:
$('.menu-wrapper > ul > li').each(function(){
$(this).data('original', $(this).text());
});
Then in mouseout part of hover read the previously stored text using data() again
$(this).find('li').fadeOut(300, function () {
var originalText = $(this).data('original');
$(this).text(originalText).fadeIn(300);
});
Several options for the new text:
Put it in markup as data attribute
<li data-alt_text="alternate word">
Then within mousenter callback of hover:
$p.fadeOut(300, function () {
$(this).text($(this).data('alt_text')).fadeIn(300);
});
Or put in array and use first loop to add the array data to element
var words=['W1','W2','W3'];
// first argument of "each" is "index"
$('.menu-wrapper > ul > li').each(function(index){
$(this).data(
{
'alt_text': words[index],
'original', $(this).text()
}
);
});
You can make use of javascripts ability to assign any property to an object (element) to store the original text instead of storing it in a single variable (or use jquery data functionality to do the same)
$('.menu-wrapper li').hover(function () {
$(this).fadeOut(300, function () {
this.originalText = $(this).text();
$(this).text('replacement word 1').fadeIn(300);
});
}, function () {
// set original text
$(this).fadeOut(300, function () {
$(this).text(this.originalText).fadeIn(300);
});
});
fiddle
For this to work, instead of binding to the .menu-wrapper div directly, you can use .menu-wrapper li to bind to the individual li elements inside the div. Afterwards the orignal text can be stored before changing it. The same can be done beforehand, storing all values, the advantage of this way is that you always store the latest value, in case the text is dynamically altered after startup.
To couple the replacement texts to the li elements, without altering the html safest would be to couple the replacement to the text. Easiest is an indexed based solution:
var replacements = ['replacement Word1', 'for word2' , 'third time\'s a charm'];
$('.menu-wrapper li').hover(function () {
var $this= $(this);
$this.fadeOut(300, function () {
$this.data('originalText', $this.text()).
text(replacements[$this.index()]).fadeIn(300);
});
}, function () {
// set original text
$(this).fadeOut(300, function () {
$(this).text($(this).data('originalText')).fadeIn(300);
});
});
fiddle
For completeness sake, this would be an alternative while using the li text (provided the text can be used as a property):
var replacements ={
WORD1 : 'replacement Word1',
WORD2 : 'for word2',
WORD3: 'third time\'s a charm'
};
$('.menu-wrapper li').hover(function () {
var $this= $(this);
$this.fadeOut(300, function () {
$this.data('originalText', $this.text()).
text(replacements[$this.text()]).fadeIn(300);
});
}, function () {
// set original text
$(this).fadeOut(300, function () {
$(this).text($(this).data('originalText')).fadeIn(300);
});
});
fiddle
Here's a short and simple solution to your problem:
var originalText;
$('.menu-wrapper').hover(function () {
var $p = $(this).find('li');
$p.fadeOut(300, function () {
this.originalText = $(this).text(); // STORES VALUE BEFORE REPLACEMENT
$(this).text('replacement word 1').fadeIn(300);
});
}, function () {
$(this).find('li').fadeOut(300, function () {
$(this).text(this.originalText).fadeIn(300);
});
});
Just store the value of that element in originalText before replacing it.
We can use two arrays to store Original text and New text. And then use $.each to loop through each of the lis and use their index to replace the text.
HTML :
<div class="menu-wrapper">
<ul>
<li>WORD1</li>
<li>WORD2</li>
<li>WORD3</li>
</ul>
</div>
jQuery :
var originaltext = ['Word1','Word2','Word3'];
var newText = ['New text1','New text2','New text3'];
$('.menu-wrapper').hover(function () {
$('.menu-wrapper li').each(function(i){
$this = $(this);
$this.html(newText[i])
});
}, function(){
$('.menu-wrapper li').each(function(i){
$this = $(this);
$this.html(originaltext[i])
});
});
jsfiddle
Since all of the other answers here use jQuery, I'll add one done with vanilla js.
To do this, we're going to need to use a javascript closure. This is used so that on completion of the fade-out, we have (a) the element just faded and (b) which is far more important, an index into the originalStrings array. (B) is the more important here, because the target element is something the animate code already has - we could easily pass the original element to the callback function. However, we really need the index or the string that corresponds to each element. The closure gives a means to do so.
The following code will fade-out all/any matching elements and then perform a fade-in after changing the text.
Using the equations found here: Math: Ease In, ease Out a displacement using Hermite curve with time constraint we can then set about making some code that will perform a smooth fade/move/scale pitch/volume slide etc, etc. I did this an ended up a few functions that facilitate simple animations. I've included minified versions of them below, for an all-in-one complete solution that relies on no other resources.
<!DOCTYPE html>
<html>
<head>
<script>
"use strict";
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded()
{
document.getElementById('goBtn').addEventListener('click', onButtonClick, false);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// animation stuff
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function cubicHermite(a,b,d,e,c){var g=a*a,f=g*a;return(2*f-3*g+1)*b+(f-2*g+a)*e+(-2*f+3*g)*d+(f-g)*c}
function interp(a,b,d,e,c){var g,f;f=e/(a/2+b+d/2);g=f*a/2;f*=b;return c<=a?cubicHermite(c/a,0,g,0,f/b*a):c<=a+b?g+f*(c-a)/b:cubicHermite((c-a-b)/d,g+f,e,f/b*d,0)}
function linear(a){return a}
function cubic(a){return interp(0.35,0.3,0.35,1,a)}
function doAnimStep(a,b,d,e,c){a<=b?(setTimeout(function(){doAnimStep(a,b,d,e,c)},d),e(a/b),a++):void 0!=c&&null!=c&&c()}
function doAnim3(totalMs, stepCallbackFunc, doneCallbackFunc)
{
var stepDelay = 1000 / 60.0; // set anim to 60 fps
var numSteps = (totalMs / stepDelay)>>0;
setTimeout( doAnim3TimeoutCallback, stepDelay );
function doAnim3TimeoutCallback()
{
doAnimStep(0, numSteps, stepDelay, stepCallbackFunc, doneCallbackFunc);
};
}
function animFadeOut(elem, callback){ doAnim3(500,function(raw){elem.style.opacity=1-cubic(raw)},callback); }
function animFadeIn(elem, callback) { doAnim3(500,function(raw){elem.style.opacity=cubic(raw)},callback); }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var replacementStrings = [ "replacement 1", "I'm next", "mee too", "fourth item" ];
function onButtonClick(evt)
{
var originalStrings = [];
var targetLiElems = document.querySelectorAll('.menu-wrapper > ul > li');
for (var i=0,n=targetLiElems.length;i<n;i++)
{
var curElem = targetLiElems[i];
originalStrings.push(curElem.innerText);
animFadeOut(curElem, createFunc(i) );
}
function createFunc(i)
{
return function(){ var curElem = targetLiElems[i]; curElem.innerText = replacementStrings[i]; animFadeIn(curElem); };
}
}
</script>
<style>
</style>
</head>
<body>
<button id='goBtn'>Change the text</button>
<div class="menu-wrapper">
<ul>
<li>WORD1</li>
<li>WORD2</li>
<li>WORD3</li>
<li>WORD4</li>
</ul>
</div>
</body>
</html>
Im trying to code a site where the objective is to click on two identical images and it hides the both the images you've managed to match to eachother.
$(document).ready(function(){
var animal1;
var animal2;
$(".memory1").on("click", function(){
animal1 = $(this).data('animal');
});
$(".memory2").on("click", function(){
animal2 = $(this).data('animal');
if (animal1==animal2){
$(this).data('animal').hide();
}
else {
alert("Wrong, Try again!");
}
});
});
so the line where its going wrong is obviously
$(this).data('animal').hide();
But I cant figure out a way to hide both images, or a better way of going about it.. :/
http://jsfiddle.net/4vgfca76/
This doesn't work the way you think it does
$(this).data('animal').hide();
When data is used with one argument, it get's the data attribute, which you should already know as you're doing it a few lines above.
What you get is the string hund etc. and that string doesn't have a hide() method.
You should be using the attributes selector to select the elements with that attribute instead
$(document).ready(function () {
var animal1, animal2;
$(".memory1").on("click", function () {
animal1 = $(this).data('animal');
});
$(".memory2").on("click", function () {
animal2 = $(this).data('animal');
if (animal1 == animal2) {
$('img[data-animal="'+animal1+'"]').hide();
} else {
alert("Fel! Försök igen");
}
});
});
that's pretty much it, how do I get the first four images from whatever url and then append them to a specified element
something like this:
$('document').ready(function(){
var thing = $.get('thing.html');
thing.slice(0,2).appendTo(".appending");
});
Try this
$('document').ready(function () {
var thing = $.get('HTMLPage.htm',
function (markup, b) {
var $page = $(markup);
$page.each(function (index, item) {
if (item.tagName == "IMG") {
$(item).appendTo(".appending");
}
});
});
});
Try this:
$('document').ready(function(){
var thing = $.get('thing.html');
thing.find('img').slice(0,4).appendTo(".appending");
});
$.get('thing.html', function(html){
//depending on what 'html' is made of, you may need to wrap it in a node
var $imgs = $(html).find('img').slice(0,4);
$(imgs).appendTo(".appending");
});
If you're expecting thing to contain HTML, try
$('document').ready(function(){
var thing = $.get('thing.html');
$(thing).filter('img').slice(0,4).appendTo(".appending");
});
.find('img') search only in descendants so if your thing contains img direcly It wouldn't work, try filter() instead http://jsfiddle.net/ouadie/UnNd9/
filter() – search through all the elements.
find() – search through all the child elements only.
http://www.mkyong.com/jquery/difference-between-filter-and-find-in-jquery/
Assuming this is some existing block level element in the dom and image is http://www.google.com/images/srpr/nav_logo25.png doing the following does not work:
$(this).append('<img>').attr('src', image).load(function() { alert('loaded'); });
Should it be done as follows?:
$(this).append($('<img>').attr('src', opts.image).load(function() { alert('loaded'); }));
Or it should be done some other way?
.append() returns the object you appended to, to get the object you appended in the chain, use .appendTo() instead:
$('<img>').appendTo(this).load(function() { alert('loaded'); })
.attr('src', opts.image);
Both versions return what was in the chain previously...but since you want to deal with the <img> use the .appendTo() form. Also, for reliable results with your load, attach your load handler before setting the src as I have above.
No, append returns the jQuery object to which you are appending. From the source (1.4.4)
append: function() {
return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 ) {
this.appendChild( elem );
}
});
}
I'd like to check ancestry using two jQuery objects. They don't have IDs, and are only going to be available as jQuery objects (or DOM nodes if you called get()). jQuery's is() only works with expressions, so this code would be ideal but will not work:
var someDiv = $('#div');
$('a').click(function() {
if ($(this).parents().is(someDiv)) {
alert('boo');
}
}
Just want to see if one element is a child of another and I'd like to avoid stepping back into DOM land if possible.
You can use the index() method to check if an element exists in a list, so would the following work?
var someDiv = $('#div');
$('a').click(function() {
if ($(this).parents().index(someDiv) >= 0) {
alert('boo');
}
}
From #index reference.
Checking for (this).parents().index(someDiv) >= 0, as #Gareth suggests, will work just fine.
However, using the jQuery ancestry plugin is way faster / more efficient.
Along those lines, parents() optionally accepts a selector itself:
$('a').click(function() {
if ($(this).parents("#div").length) {
alert('boo');
}
});
One way would be to use the filter function
$('a').click(function() {
$(this).parents().filter(function() {
return this == someDiv[0];
}).each(function() {
alert('foo');
})
}
I think you may also be able to get away with using jQuery.inArray
if ($.inArray( someDiv, $(this).parents() ) ) {
alert('boo');
}
Would you not get the result you want from simply using a CSS selector?
$( '#div a' ).click( function() { ... } );
Try this:
var someDiv = $('#div');
$('a').click(function() {
if ($.inArray($(this).parents().get(), someDiv.get(0)) {
alert('boo');
}
}
var $element = $('a');
while ($element && !$element.is('someDiv')) {
var $element = $element.parent();
};