How to select child from parent by class starting with $(this) - javascript

I'm creating a custom jQuery plugin to add a few images above an input box. The highlight class are working perfectly, but I need help with the selectors on the .toggle() for showing and hiding the INPUTBANNER class.
jQuery.fn.inputmenu = function() {
function createInputMenu(node) {
$(node).bind('focus', function() {
$(this).parent().toggleClass('highlight');
//SHOW INPUTBANNER CLASS
$(this).parent().('.inputbanner').toggle();
});
$(node).bind('blur', function() {
$(this).parent().toggleClass('highlight');
//HIDE INPUTBANNER CLASS
$(this).parent().('.inputbanner').toggle();
});
$(node).parent().append('<div class="inputbanner">some images here</div>');
}
return this.each(function() {
createInputMenu(this);
});
};

This seems to be what you are after, you don't have to go back up to the parent and then go back down to the .inputbanner to select it, since inputbanner is a sibling you can just do:
// use .prev() if the element is before
$(this).next('.inputbanner')
Also as a side note, you should wrap your plug-in like so (so that there are no collisions with the $ identifier)
(function($) {
$.fn.inputmenu = function() {
// plugin implementation here
}
})(jQuery);

i'd like to see the state of your DOM / html markup to test it out, but try this:
jQuery.fn.inputmenu = function () {
return this.each(function () {
var $node = $(this);
$node.parent().append('<div class="inputbanner" style="display:none">some images here</div>');
var $nodeImageContainer = $node.parent().find('div.inputbanner').eq(0);
$node.bind('focus', function () {
//SHOW INPUTBANNER CLASS
$nodeImageContainer.addClass('highlight').show();
})
.bind('blur', function () {
//HIDE INPUTBANNER CLASS
$nodeImageContainer.removeClass('highlight').hide();
});
});
};

Related

Show a sub-menu using JQuery

I need some help making a sub-menu appear within 2s after the page loads instead of when the user clicks on it. I'm using JQuery. That file is the core of the website. I need it to stay opened.
Here's the code I have at the moment, I tried to change that on.Click event but it didn't work.
The handleSidenarAndContentHeight(); function resizes the menu items after the sub-menu appears.
jQuery('.page-sidebar li > a').on('click', function (e) {
if ($(this).next().hasClass('sub-menu') === false) {
return;
}
var parent = $(this).parent().parent();
parent.children('li.open').children('a').children('.arrow').removeClass('open');
parent.children('li.open').children('a').children('.arrow').removeClass('active');
parent.children('li.open').children('.sub-menu').slideUp(350);
parent.children('li').removeClass('open');
parent.children('li').removeClass('active');
var sub = jQuery(this).next();
if (sub.is(":visible")) {
jQuery('.arrow', jQuery(this)).removeClass("open");
jQuery(this).parent().removeClass("active");
sub.slideUp(350, function () {
handleSidenarAndContentHeight();
});
} else {
jQuery('.arrow', jQuery(this)).addClass("open");
jQuery(this).parent().addClass("open");
sub.slideDown(350, function () {
handleSidenarAndContentHeight();
});
}
e.preventDefault();
});
Working with a 2 second timeout should do the trick!
jQuery(document).ready(function(){
// Open Parent here
setTimeout(function(){
// Open Child here
}, 2000)
});
There is a simple javascript function you can use, the setTimeout function.
The code follows like this :
setTimeout(function() {yourFunctyion();}. delayTimeInMiliseconds);
This will call your function after the number of second(in ms).
There is also a plugin I've used. It has oneTime and everyTime methods.
jQuery timers plugin

Replacing multiple <li> text simultaneously on hover using Javascript (not CSS!)

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>

simplify jQuery selector highlight

I have over 100 videos and I use a function to highlight the links clicked. The code thought is very long and I feel like there must be a way to simplify it into a for loop or something. Any idea?
var vid_all0 = $('#vid_link0, #vidtop_link0, .vidtop_link0, #vidmob_link0, #link0'); //cache selector
vid_all0.click(function () {
$('[id^=vid_link],[id^=vidtop_link],[id^=vidmob_link], .vidtop_link0').css('background-color', 'inherit');
vid_all0.css('background-color', '#A9CDEB'); //change color of all elements
$('.vidtop_link0').css('background-color', 'inherit');
});
var vid_all1 = $('#vid_link1, #vidtop_link1, #vidmob_link1,#link10'); //cache selector
vid_all1.click(function () {
$('[id^=vid_link],[id^=vidtop_link],[id^=vidmob_link]').css('background-color', 'inherit');
vid_all1.css('background-color', '#A9CDEB'); //change color of all elements
});
var vid_all2 = $('#vid_link2, #vidtop_link2, #vidmob_link2,#link19'); //cache selector
vid_all2.click(function () {
$('[id^=vid_link],[id^=vidtop_link],[id^=vidmob_link]').css('background-color', 'inherit');
vid_all2.css('background-color', '#A9CDEB'); //change color of all elements
});
...
it goes up to 15
Give all those elements the same class, then use all elements with that class like
$(".vidtop").on("click", function()
{
// Do something with their CSS
});
If you can't modify your HTML with a class, something like this should work:
for (var i=1; i=99; i==;) {
$(vid_all + i).click(function () {
$(this).find('[id^=vid_link],[id^=vidtop_link],[id^=vidmob_link]').css('background-color', 'inherit');
});
}
I'm not sure if I understood your code correctly, but this would seem to be a simpler version:
function doStuff(links, additional) {
links.click(function() {
$('[id^=vid_link],[id^=vidtop_link],[id^=vidmob_link]' + (additional ? "," + additional : "")).css('background-color', 'inherit');
links.css('background-color', '#A9CDEB');
if (additional) {
additional.css('background-color', 'inherit');
}
});
}
// vid_all0
doStuff($('#vid_link0, #vidtop_link0, .vidtop_link0, #vidmob_link0, #link0'), $('.vidtop_link0'));
// vid_all1
doStuff($('#vid_link1, #vidtop_link1, #vidmob_link1,#link10'));
// vid_all2
doStuff($('#vid_link2, #vidtop_link2, #vidmob_link2,#link19'));
// etc.

jQuery switching between more than two classes

I've already posted a question about jQuery toggle method here
But the problem is that even with the migrate plugin it does not work.
I want to write a script that will switch between five classes (0 -> 1 -> 2 -> 3 -> 4 -> 5).
Here is the part of the JS code I use:
$('div.priority#priority'+id).on('click', function() {
$(this).removeClass('priority').addClass('priority-low');
});
$('div.priority-low#priority'+id).on('click' ,function() {
$(this).removeClass('priority-low').addClass('priority-medium');
});
$('div.priority-medium#priority'+id).on('click', function() {
$(this).removeClass('priority-medium').addClass('priority-normal');
});
$('div.priority-normal#priority'+id).on('click', function() {
$(this).removeClass('priority-normal').addClass('priority-high');
});
$('div.priority-high'+id).on('click', function() {
$(this).removeClass('priority-high').addClass('priority-emergency');
});
$('div.priority-emergency'+id).on('click', function() {
$(this).removeClass('priority-emergency').addClass('priority-low');
});
This is not the first version of the code - I already tried some other things, like:
$('div.priority#priority'+id).toggle(function() {
$(this).attr('class', 'priority-low');
}, function() {
$(this).attr('class', 'priority-medium');
}, function() {
...)
But this time it only toggles between the first one and the last one elements.
This is where my project is: strasbourgmeetings.org/todo
The thing is that your code will hook your handlers to the elements with those classes when your code runs. The same handlers remain attached when you change the classes on the elements.
You can use a single handler and then check which class the element has when the click occurs:
$('div#priority'+id).on('click', function() {
var $this = $(this);
if ($this.hasClass('priority')) {
$this.removeClass('priority').addClass('priority-low');
}
else if (this.hasClass('priority-low')) {
$this.removeClass('priority-low').addClass('priority-medium');
}
else /* ...and so on... */
});
You can also do it with a map:
var nextPriorities = {
"priority": "priority-low",
"priority-low": "priority-medium",
//...and so on...
"priority-emergency": "priority"
};
$('div#priority'+id).on('click', function() {
var $this = $(this),
match = /\bpriority(?:-\w+)?\b/.exec(this.className),
current = match && match[0],
next = nextPriorities[current];
if (current) {
$this.removeClass(current).addClass(next || 'priority');
}
});
[edit: working demo]
Assuming you have 'priority' as the default class already on the element at the initialization phase, this will cycle through the others:
$('div#priority' + id)
.data('classes.cycle', [
'priority',
'priority-low',
'priority-medium',
'priority-normal',
'priority-high',
'priority-emergency'
])
.data('classes.current', 0)
.on('click', function () {
var $this = $(this),
cycle = $this.data('classes.cycle'),
current = $this.data('classes.current');
$this
.removeClass(cycle[current % cycle.length])
.data('classes.current', ++current)
.addClass(cycle[current % cycle.length]);
});
I have tried myself to do this with the sole help of toggleClass() and didn't succeeded.
Try my method that declares an array with your five classes and toggles dynamically through
them.Do adapt to your own names.
//variable for the classes array
var classes=["one","two","three","four","five"];
//add a counter data to your divs to have a counter for the array
$('div#priority').data("counter",0);
$(document).on('click','div#priority',function(){
var $this=$(this);
//the current counter that is stored
var count=$this.data("counter");
//remove the previous class if is there
if(($this).hasClass(classes[count-1])){
$(this).removeClass(classes[count-1]));
}
//check if we've reached the end of the array so to restart from the first class.
//Note:remove the comment on return statement if you want to see the default class applied.
if(count===classes.length){
$this.data("counter",0);
//return;//with return the next line is out of reach so class[0] wont be added
}
$(this).addClass(classes[count++]);
//udpate the counter data
$this.data("counter",count);
});
//If you use toggleClass() instead of addClass() you will toggle off your other classes.Hope is a good answer.

jQuery extension: why does it not work on all matching elements?

I have an extension going like:
$.fn.crazything = function() {
var self = $(this);
// do some crazy stuff
return self;
}
And when I call it like:
$("div.crazydiv").crazything();
It works, but only on the first matching div. If I have more than one div on the page, I need to do:
$("div.crazydiv").each(function(i) { $(this).crazything (); });
Why is this, and how can I rewrite my extension to work on multiple divs?
Most jQuery plugins use this pattern which handles your crazy stuff:
(function($) {
$.fn.crazything = function() {
// allow setup on jQuery objects that conatin multiple elements:
return this.each(function() {
// this function is called once for each element in the jQuery object
var self = $(this);
// do some crazy stuff
});
};
})(jQuery);

Categories