JQuery Toggle Divs Based On Index not retoggle when clicked twice - javascript

and thank you for reading this question. Let me preface this by saying that I am not a programmer and only have tried to learn javascript to make my own websites look and function the way I want.
I have a page with several hidden divs. I'm using elements with the same class and different targets to trigger this Jquery
jQuery(function () {
jQuery('.nav').click(function () {
var index = $(this).index(),
newTarget = jQuery('.targetDiv').eq(index);
jQuery('.targetDiv').not(newTarget).slideUp('fast')
newTarget.delay('fast').slideToggle('fast')
return false;
})
});
So my ".targetDiv"s look like this:
<div class=".targetDiv" style="display:none">div1</div>
<div class=".targetDiv" style="display:none">div2</div>
<div class=".targetDiv" style="display:none">div3</div>
And the "navigation" would look something like this
link1
link2
link3
This is not my code, and I got it from here: http://forum.jquery.com/topic/slidetoggle-multiple-divs-31-5-2013
It works exactly as it is supposed to and I have no complaints about that. When you click on a link, the corresponding div toggles, but when you click the same div again right afterwards, it toggles again and slides up (which is how the code is written). I want to stop that from happening, and since I am new to Javascript and Jquery I can't figure out how to do it. My non programmer mind assumes that there should be some kind of if else clause, where you would say:
if .targetDiv is :visible, then do not toggle newTarget. However when I tried to do that, it did not work.
if($(".targetDiv").is(":hidden")) { jQuery(function () {
jQuery('.nav').click(function () {
var index = $(this).index(),
newTarget = jQuery('.targetDiv').eq(index);
jQuery('.targetDiv').not(newTarget).slideUp('fast')
newTarget.delay('fast').slideToggle('fast')
return false;
})
});}
else {alert("already open")}
I don't know how else I should handle this, but it must be possible and I am probably just thinking of how to achieve what I want in entirely the wrong way. I understand very little about javascript, but I am not asking for someone to write this for me, I'd rather have someone tell me what it is that I am doing that is incorrect, then explain what it is I should be trying to do. Then I can use google to search for the way to achieve that.
Again, thank you for taking the time to read this and hopefully I've been detailed enough for some answers.

You just need to wrap the two 'slide' lines in the if statement, like so:
if (!newTarget.is(':visible'))
{
jQuery('.targetDiv').not(newTarget).slideUp('fast');
newTarget.delay('fast').slideToggle('fast');
}
You may also want to fix a few issues with the html, e.g. take the periods out of your class names. When querying for DOM elements, the "." means, "Find the things that have a class called _[whatever follows the dot]". Don't put dots in the classes themselves.
You may also want to take out the href attributes of the <a> tags. They aren't necessary.
Here's a working JSFiddle. Cheers!

Related

How to be able to click on a TR and edit personalia

I'm studying webdevelopment and I'm doing a single page application right now. We are using JavaScript, and I can`t use jquery, bootstrap, etc. I have googled, seen the videos from the lectures, but I am still blank as a canvas.
The problem is I need to make a contactregistre. You should be able to click on the contacts, a different section of the page should be made active where you will be able to edit the contacts and see where they live. The map is OK, but I don`t know how I can make this happen, I find no examples about this which does not suggest using jquery.
We have guessed something like this, but it is probably wrong:
document.querySelector("tr").addEventListener('click' , e => {
document.querySelector('editContact')
function editContact(contact) {
let editContact = document.querySelector("#searchcontact tr");
editContact.innerHTML = "TR";
let form = document.editContact()
}
})
Thanks so much in advance!
There are numerous ways to do this. This is not a complete solution, but should get you started:
Typically you'd have a text input already in your table <input name="username_1" class="hidden" type="text">, which is hidden, along side the content, like <span id="username_1">MAREN</span>
Then you'd have some CSS to hid things:
.hidden {
display:none;
}
So on the click even you'd add the hidden classname to the SPAN and remove it from the INPUT. This visually swaps the static value for the label.
There's more to it after that, but give it a try.

Where each variable is equal to spans content add class

The Question and Codes
I am struggling with the below code:
$('.rdsubs-mysubscriptions table tbody tr td a').each(function() {
var subItem = $(this).html();
//console.log(subItem);
var subItemStripped = subItem.substring(12);
console.log(subItemStripped);
$('body').find('span:contains("subItemStripped")').addClass('HELLO');
}); // end of each function
When I check the console for subItemStripped then it shows this:
Framework
Content
Slideshow
Which means (in my head at least ;-)) that for each span that is inside the body it should find one of these subItemStripped and where it finds a match it should add the class hello but this is not happening.
Actually, nothing is happening.
When I change this line:
$('body').find('span:contains("subItemStripped")').addClass('HELLO');
to
$('body').find('span:contains("Framework")').addClass('HELLO');
It works nicely. So am I putting the variable subItemStripped wrongly in there or has it something to do with the .each() function.
I tried the below things to make it work
With the above code I tried a couple of variations before I came here:
$('body').find('span:contains(subItemStripped)').addClass('HELLO');
$('body').find("span:contains('subItemStripped')").addClass('HELLO');
I also tried it with completely different sets of code I gathered from other SO posts but none of those worked. Why I don't know.
$("span").filter(function() {
return $(this).text() === subItemStripped;
}).addClass("hello");
$("span").filter(function() {
return $(this).text() === subItemStripped;
}).css("font-size", "6px");
Why do I need this
I know I don't have to explain why I need this but it could be useful in coming up with other great ideas if the above is not feasible.
I have a webpage and on that page is a menu filled with products that a user can download if he/she has access.
Each menu item has a span with the title in it. Those titles are built up like: Framework Content Slideshow
On this same page is also a component that shows all the users subscriptions.
With the above code, I look to all the subscriptions of the user. Which returns
CompanyName Framework CompanyName Content CompanyName Slideshow
Then I Strip .substring(12) all the parts that I know are not present inside the menu. Which leaves me with Framework Content Slideshow
At this point, I know that some menu titles and the stripped item are the same and for every match, I want to add a class upon which I can then add some CSS or whatnot.
Hopefully, the question is clear and thanks to everyone in advance for helping me out.
#gaetanoM You are completely right. Right after I posted the question I came on this site:
jQuery contains() with a variable syntax
And found the answer which is the same as you are saying!
$('body').find("span:contains('" + subItemStripped + "')").addClass('HELLO');
Thanks so much!
#gaetanoM Can you make your comment in an answer? Then I can select it as the accepted answer. I am answering this question now just to make sure it has an answer. As people get punished for asking questions that don't get answers.

Using addEventListener on multiple elements, avoid TypeError when particular element not found

I'm using two simple addEventListener mouseenter and mouseleave functions respectively to play and stop animations (Bodymovin/SVG animations, though I suspect that fact is irrelevant).
So, the following works fine:
document.getElementById('animationDiv').addEventListener('mouseenter', function(){
animation.play();
})
(The HTML couldn't be simpler: The relevant part is just an empty div placeholder filled by script - i.e., <div id="animationDiv"></div>.
I can place that in the same file as the one that operationalizes the animation code, or I can place it in a separate "trigger" file, with both files (and other others necessary to processing) loaded in the site footer.
The problem arises when I need to be able to set triggers for any of multiple similar animations that may or may not appear on a given page.
If only one of two animatable elements are present on a page, then one of two sets of triggers will throw an error. If the first of two such triggers is not present, then the second one will not be processed, meaning that the animation will fail. Or at least that's what it looks like to me is happening.
So, just to be clear, if I add the following two triggers for the same page, and the first of the following two elements is present, then the animation will play on mouseenter. If only the second is present, its animation won't be triggered, apparently because of the error thrown on the first.
document.getElementById('firstAnimationDiv').addEventListener('mouseenter', function(){
firstAnimation.play();
})
document.getElementById('secondAnimationDiv').addEventListener('mouseenter', function(){
secondAnimation.play();
})
At present I can work around the problem by creating multiple trigger files, one for each animation, and setting them to load only when I know that the animatable element will be present, but this approach would get increasingly inefficient when I am using multiple animations per page, on pages whose content may be altered.
I've looked at try/catch approaches and also at event delegation approaches, but so far they seem a bit complicated for handling this simple problem, if appropriate at all.
Is there an efficient and flexible standard method for preventing or properly handling an error for an element not found, in such a way that subsequent functions can still be processed? Or am I missing something else or somehow misreading the error and the function failure I've been encountering?
WHY I PICKED THE ANSWER THAT I DID (PLUS WORKING CODE)
I was easily able to make the simple, directly responsive answer by Baoo work.
I was unable to make the answers below by Patrick Roberts and Crazy Train work, though no doubt my undeveloped js skills are entirely at fault. When I have the time, or when the issue next comes up for me in a more complex implementation (possibly soon!), I'll take another look at their solutions, and see if I can either make them work or if I can formulate a better question with fully fledged coding examples to be worked through.
Finally, just to make things clear for people who might be looking for an answer on Bodymovin animations, and whose js is even weaker than mine, the following is working code, all added to the same single file in which a larger set of Bodymovin animations are constructed, relieving me of any need to create separate trigger files, and preventing TypeErrors and impaired functionality.
//There are three "lets_talk" animations that can play - "home," "snug," and "fixed"
//and three types of buttons needing enter and leave play and stop triggers
let home = document.getElementById('myBtn_bm_home');
if (home) home.addEventListener('mouseenter', function() {
lets_talk_home.play();
});
if (home) home.addEventListener('mouseleave', function() {
lets_talk_home.stop();
});
let snug = document.getElementById('myBtn_bm_snug');
if (snug) snug.addEventListener('mouseenter', function() {
lets_talk_snug.play();
});
if (snug) snug.addEventListener('mouseleave', function() {
lets_talk_snug.stop();
});
let fixed = document.getElementById('myBtn_bm_fixed');
if (fixed) fixed.addEventListener('mouseenter', function() {
lets_talk_fixed.play();
});
if (fixed) fixed.addEventListener('mouseleave', function() {
lets_talk_fixed.stop();
});
At typical piece of underlying HTML (it's generated by a PHP function taking into account other conditions, so not identical for each button), looks like this at the moment - although I'll be paring away the data-attribute and class, since I'm not currently using either. I provide it on the off-chance that someone sees something significant or useful there.
<div id="letsTalk" class="lets-talk">
<a id="myBtn" href="#"><!-- a default-prevented link to a pop-up modal -->
<div class="bm-button" id="myBtn_bm_snug" data-animation="snug"></div><!-- "snug" (vs "fixed" or "home" is in both instances added by PHP -->
</a>
</div>
Obviously, a more parsimonious and flexible answer could be - and probably should be - written. On that note, correctly combining both the play and stop listeners within a single conditional would be an obvious first step, but I'm too much of a js plodder even to get that right on a first or second try. Maybe later/next time!
Thanks again to everyone who provided an answer. I won't ask you to try to squeeze the working solution into your suggested framework - but I won't ask you not to either...
Just write your code so that it won't throw an error if the element isn't present, by simply checking if the element exists.
let first = document.getElementById('firstAnimationDiv');
if (first) first.addEventListener('mouseenter', function() {firstAnimation.play();});
You could approach this slightly differently using delegated event handling. mouseover, unlike mouseenter, bubbles to its ancestor elements, so you could add a single event listener to an ancestor element where every #animationDiv is contained, and switch on event.target.id to call the correct play() method:
document.getElementById('animationDivContainer').addEventListener('mouseover', function (event) {
switch (event.target.id) {
case 'firstAnimationDiv':
return firstAnimation.play();
case 'secondAnimationDiv':
return secondAnimation.play();
// and so on
}
});
You could also avoid using id and use a more semantically correct attribute like data-animation as a compromise between this approach and #CrazyTrain's:
document.getElementById('animationDivContainer').addEventListener('mouseover', function (event) {
// assuming <div data-animation="...">
// instead of <div id="...">
switch (event.target.dataset.animation) {
case 'first':
return firstAnimation.play();
case 'second':
return secondAnimation.play();
// and so on
}
});
First, refactor your HTML to add a common class to all of the placeholder divs instead of using unique IDs. Also add a data-animation attribute to reference the desired animation.
<div class="animation" data-animation="first"></div>
<div class="animation" data-animation="second"></div>
The data- attribute should have a value that targets the appropriate animation.
(As #PatrickRobers noted, the DOM selection can be based on the data-animation attribute, so the class isn't really needed.)
Since your animations are held as global variables, you can use the value of data-animation to look up that variable. However, it would be better if they weren't global, but were rather in a common object.
const animations = {
first: null, // your first animation
second: null, // your second animation
};
Then select the placeholder elements by class, and use the data attribute to see if the animation exists, and if so, play it.
const divs = document.querySelectorAll("div.animation");
divs.forEach(div => {
const anim = animations[div.dataset.animation];
if (anim) {
anim.play(); // Found the animation for this div, so play it
}
});
This way you're guaranteed only to work with placeholder divs that exist and animations that exist.
(And as noted above, selection using the data attribute can be done const divs = document.querySelectorAll("div[data-animation]"); so the class becomes unnecessary.)

Hiding / displaying content in javascript

I am a very new web developer. I'm doin fine with HTML and CSS and im trying to learn javascript. Im very new 1 month ago I never coded a single line, and I've made 2 or 3 websites already but none are JS capable.
I know I can do this in jquery and have already, but I want to Understand it and I want to be able to do it in vanilla javascript.
So here it is . I am making a simple game for practice, all it does is ask questions and change elements from display = none to display = block based on a click event to two big YES or NO buttons.
So.... this first code targeting yes_button works fine. The instructions Hide and the first questiong (Q1) appears.
the No button does not work, and I cannot figure out why, it is essentially the same code targeting a different element.
Things I checked :
the element id/selectors are correct
camelCase is good,
no unclosed braces,
semi colons are good,
is the mistake in the logic somewhere?
window.onload = function() {
yes_button.onclick= function() {
document.getElementById("instructions").style.display="none";
document.getElementById("q1").style.display="block";
};
no_button.onClick=function() {
document.getElementById("instructions").style.display="none";
document.getElementById("nointro").style.display="block";
};
};
You can remove the window.onload function call. You can place your JavaScript imports just before the closing tag so the dom is ready before you attempt to click on the button.
Your JavaScript Events should look like this:
document.getElementById("yes_button").onclick = function(){
document.getElementById("instructions").style.display= "none";
document.getElementById("q1").style.display = "block";
};
document.getElementById("no_button").onclick = function(){
document.getElementById("instructions").style.display="none";
document.getElementById("nointro").style.display="block";
};

jquery ui.slider: add click event to display/hide handle

I am completely new to javascript and jquery. My programming knowledge is... nonexistent, I just started some days ago with some simple tasks like replacing a CSS class or toggling a div. So I want to apologize if I'm treading on someones toes by asking newbie-questions in here. But I hope that someone can help me and solve my problem.
I need to implement some sort of visual analog scale for a survey; ui.slider is perfect for that one. But I need the handle to be hidden by default. When the user clicks on the slider, the handle shall appear in the proper position. That should be fairly simple - at least I hope so - by just hiding the handle with CSS and changing it by a click event on the slider.
I use the following piece of code to wrap a normal div (a div is needed in my understanding to apply the jquery slider.js) to my input elements (they should be - at least visually - replaced by the slider) and pass the value of the slider to the input elements (needed for passing the values to a form). that works properly. (I do that instead of just putting a div in my DOM by default because I cannot influence some PHP scripts that will generate form elements of the survey and so on)
$(function () {
$.each($('.slider'),
function () {
obj = $(this);
obj.wrap('<div></div>');
obj.parent().slider({
change: function (event, ui) {
$('input', this).val(ui.value);
}
});
});
});
Hiding the slider-handle can be done by CSS as described above by changing style properties of a.ui-slider-handle. but when I add a normal click event to the slider (.ui-slider) that changes CSS properties of the handle, nothing happens. As far as my basic knowledge goes it should have something to do with the click event not working on generated DOM elements. Am I right with that one? And if yes: how can I solve this problem? Could someone provide me a piece of code for my function and explain it so I might comprehend what's exactly going on?
I read a tutorial about events on learningjquery.com but I have not made enough progresses the last few days since I started working with JS/jquery to comprehend the steps and translate it into my example/problem. And I am running out of time (I need this for a survey I have to make asap, that's why I hope someone could give me a hint so I can solve this little issue somehow).
Any reason you can't just include the show on the change event rather than a click? It's a bit cleaner code-wise rather than including a whole new event.
$(function() {
$('.slider').wrap('<div></div>').parent().slider({
change: function(event, ui) {
$('input', this).val(ui.value);
$('.ui-slider-handle').show();
}
});
});
Also, there was a bit of redundancy in the code - most jQuery functions return the object itself, so you can chain them. And you don't need that each function, since most jQuery functions also, when applied to a collection, run on all of them :)

Categories