I am very new to Javascript/JQuery and I am going through the code of a colleague of mine. He has created the very common "FAQ" list on a website where the answer is not immediately displayed, only the question until the user clicks the arrow, it then shows the answer.
I am trying to figure out how he got this to work. I understand all the HTML and the CSS (it looks fine), but I can't seem to understand how to get the Jquery to work or where to call the code when the user press the arrow on the question to display the answer.
The code he seemed to use is at the bottom. Once again, all the HTML and CSS is linked up so I think it is a matter of simply how to use the code.
If anyone can offer any help, that would be appreciated.
Thanks!
$("#faq").on("click", "div > a", function() {
return $(this).next().toggle().closest("li").toggleClass("arrow_up"), !1
})
Let me annotate his code for you. It uses some shorthand that can be a little confusing for beginners.
// Select the element with an id attribute of "faq".
// When an <a> which is a first-level child of a div inside of
// #faq is clicked, perform the following actions
$("#faq").on("click", "div > a", function() {
// "this" references the <a> tag.
// $(this) wraps it in a jQuery wrapper
return $(this)
// get the next sibling of the <a>. It is our <p>
.next()
// switches the element.style.display between none and block
.toggle()
// get the nearest parent <li> element.
.closest("li")
// add or remove the arrow_up CSS class
.toggleClass("arrow_up"), !1 // !1 is a shortcut for false
});
I have a full distilled and annotated reproduction in this jsFiddle link
He also uses a shorthand for return false as well. Notice that the body of his event handler is all on one line. He could have split it into two lines, but he used the comma operator to make it fit on just one.
$(this).next().toggle().closest("li").toggleClass("arrow_up");
return false; // false === !1;
Returning false from a jQuery event handler prevents the event from reaching parent elements in the DOM.
That means:
//Bind a click function on every a tag in #faq
$("#faq").on("click", "div > a", function() {
// Toggle Class (so if the closest <li> Element has class "arrow_up" then remove it, otherwise add it)
return $(this).next().toggle().closest("li").toggleClass("arrow_up"), !1
Check out here for Example "Bootstrap Collapse" which is exactly the same you are looking for:
http://getbootstrap.com/javascript/#collapse
There is also a Demo Code provided and you see how to handle that.
Related
On a web page we have a list of profiles. On the right hand side of the profile is some text, followed by an arrow img#arrow.
When img#arrow is clicked, we have the following jQuery we hope to run:
However, the corresponding .bottom-sec is not toggling.
jQuery('#arrow').click(function(){
var $parent = $(this).parent();
$($parent).addClass('active');
jQuery($parent +' .bottom-sec').toggle();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="profile-right">
<h2>Bob Brown</h2>
<h3>Non-Executive Chairman</h3>
<p>Intially showing text.</p>
<div class="bottom-sec" style="display: none;">
<p>Initially hidden text.</p>
</div>
<img id="arrow" src="/wp-content/themes/wtc/images/icons/down-arrow-circle-hi.png">
</div>
Problem
The problem with your code is exactly what the comment on your question is saying, but he didn't explain anything:
You're combining two different ways of selecting elements. One is with selectors, the other is traversing. You're using them in a way which isn't possible (the $parent + ' .bottom-sec' part). The comment linked to a jQuery page about traversing which you should definitely read! It tells you a lot about how to use traversing functions, which you could use!
Solution
There are multiple solutions to this, but I'll write down the one I think is the best:
First of all, change the HTML a bit. I've removed the element style of .bottom-sec and changed the id of the image to a class, because you have multiple images with the same id on the page, which is not a recommended thing to do. Classes can occur more than once, id's cannot.
<div class="profile-right">
<h2>Bob Brown</h2>
<h3>Non-Executive Chairman</h3>
<p>Intially showing text.</p>
<div class="bottom-sec">
<p>Initially hidden text.</p>
</div>
<img class="arrow" src="/wp-content/themes/wtc/images/icons/down-arrow-circle-hi.png">
</div>
I've reduced the JavaScript to the following. Note that is just reduced to one line, where a click on the .arrow element goes searching for the closest .profile-right parent. If, for whatever reason, you decide to change the HTML and the .arrow element is no longer a child of the .profile-right, this code still works. The only thing it does is toggle an active class on the .profile-right.
jQuery(document).on('ready', function() {
jQuery('.arrow').on('click', function(){
jQuery(this).closest('.profile-right').toggleClass('active');
});
});
The document ready listener was added because of OP's comment.
With CSS, we can use the new .active class to show or hide the element.
.profile-right .bottom-sec {
display: none
}
.profile-right.active .bottom-sec {
display: block
}
Original Code Fix
If for some reason you wanted to use your original code, this is how it should be:
// Nothing wrong about this part.
// Your only worry should be that there could be
// multiple elements with the same ID, which is something really bad.
jQuery('#arrow').click(function(){
// This part is correct, no worries
var $parent = $(this).parent();
// Removed the $(...), because $parent is already a jQuery object
$parent.addClass('active');
// Changed the selector to a find function
$parent.find('.bottom-sec').toggle();
});
You could also combine all of the code inside the listener function to just one line:
jQuery('#arrow').click(function(){
$(this).parent().addClass('active').find('.bottom-sec').toggle();
});
Change your js code like below.
jQuery('#arrow').click(function(){
var $parent = $(this).parent();
$($parent).addClass('active');
jQuery($parent).find('.bottom-sec').toggle();
});
In your event listener you can catch the element (the down arrow) that triggered the event. It will be referred as this.
Then you can go through the DOM tree using .next() and .parent() to access the <div> to toggle.
Note: you may need more functions than the one I explained above.
Note 2: without code or more detailed information, we can't help you further, I will edit this answer if you add details.
I'm working on a javascript code which does :
$('div').html(<some text>).find('>')
Looking at the jQuery documentation, I can't understand what find('>') is supposed to do.
Moreover, when experimenting in navigator console, I get strange results :
$('div').html('to<br/>to').find('>') -> [ <br>, <br>, <br>]
$('div').html('to<a/>to').find('>') -> [ <a></a>, <a></a>, <a></a>]
Why a 3 times repetiton ?
So, can anyone enlighten me about this strange find('>') ?
> is the Child Combinator CSS selector. .find('>') will pull all direct children of the element.
As mentioned in comments, the repetitions must be due to your document having multiple div elements.
Update
From your comment:
I thought the line was creating a div then setting some html into it.
$('div') itself selects all div elements which exist within document. If you want to create a div element, you can instead do this:
$('<div/>', { html: 'to<br/>to' });
If you're new to jQuery, I'd strongly advise checking out http://try.jquery.com and http://learn.jquery.com.
As someone pointed out, '>' selects the child elements of an element.
Why 3? Because surely you have 3 divs, so
$('div') //selects 3 divs
.html(...) // adds content to each div
.find('>'); //return the direct descendants of each element in the jQuery object
//as a new jQuery object
This question is connected with that
This code hides div when user type data to inputs and focus on another div
$(".Q,.A").blur(function(e) {
if ($(this).val().length > 0 && $(this).siblings("input").val().length > 0) {
$(this).parent().fadeOut(1000);
getData("ajaxPHP/insertNewWords.php?q='" + $(this).siblings('input').val() + "'&a='" + $(this).val() + "'&zestawID="+zestawID, "console");
$(".main").append("<div><input type='text' class='Q'></input><input type='text' class='A'></input></div><br>");
}
});
And when user enter data, this div hides and script creates new div (so user can enter infinite amount of data).
The problem is: new created divs don't hide.
So what should I do, if I want to involve new created divs into "$(".Q,.A")"?
The problem is (as I understand it), that you have a behaviour attached to a set of nodes on your page, and new nodes added to the page do not pick up this behaviour.
This is because of the way JQuery works. When you define a selector like $(".Q,.A") this selector evaluates to a set of known nodes on your page. The code that follows only applies to those found elements. This selector is never evaluated again, so any new nodes never get a chance to gain your desired behaviour.
The solution is to get JQuery to re-evaluate the selector every time the event occurs. So you need to listen for the event globally, then filter to only handle the elements that match your selector.
The correct way to do this is on
$(document).on("blur", ".Q,.A", function(){ ... });
See: http://jsfiddle.net/sAT6L/
Live has some discussion on how it used to be done in each version of JQuery.
Note: You should be able to restrict the scope to something more local than $(document).
You can use jquery's .on method on the parent container, because events "bubble" to the parent container. The on function also allows you to specify a selector to filter the children elements, which gets applied dynamically, so you can use your ".Q,.A" selector there:
$(document).ready(function(){
$("#container").on("blur", ".Q,.A", function(e){
if($(this).val().length>0 && $(this).siblings("input").val().length>0){
$(this).parent().fadeOut(1000);
$("#container").append('<div><input type="text" class="Q"><input type="text" class="A"></div>');
}
});
});
Fiddle: http://jsfiddle.net/rK3HS/1/
first time posting here. I'm a beginner in jquery and i ran into some grey area. Hopefully i can find my answer here and learn from it also :)
So i have a let's say 10 different div. All has the same class. And everytime I click on the div it has to add another class (in this case background-color in css). For that I have this:
$(document).ready(function() {
$(".menucardmenu").click(function(){
if($(this).hasClass("menucardmenu")) {
$(this).addClass("backgroundmenucard");
}
else {
alert ("condition false");
}
});
});
But the question now is, how can i make that only one div can have that background-color (in my case backgroundmenucard). Depending one which div the user click, that div will have the background-color, and the previous div (that was clicked) should reset it back to normal. I can do it with this right?:
$(this).removeClass("backgroundmenucard");
does anyone know the answer to this???
Regards,
Andrew
try the following:
$(".menucardmenu").click(function(){
$(".backgroundmenucard").removeClass("backgroundmenucard");
$(this).addClass("backgroundmenucard");
});
Demo: http://jsfiddle.net/r2Sua/
(I remove the if because it's useless in this case)
Remove from all...
$(".menucardmenu").removeClass("backgroundmenucard");
Then add to this
$(function() // shorthand for document ready
{
var $divs = $('div.menucardmenu'), // standard jQuery "cache" idiom
BG_CLASS = 'backgroundmenucard'; // stay DRY, less prone to typos
$divs.live('click', function() // use .live to bind only 1 event listener
{
// remove the class from all divs
$divs.removeClass(BG_CLASS);
// add the class to this div
$(this).addClass(BG_CLASS);
}
});
});
The if($(this).hasClass("menucardmenu")) check is completely unnecessary since you're already selecting elements which have that class. It will always evaluate to true.
$('.menucardmenu').click(function(){
$('.menucardmenu').removeClass('backgroundmenucard');
$(this).addClass('backgroundmenucard');
});
Another option would be:
$(".menucardmenu").not(this).removeClass("backgroundmenucard");
$(this).addClass("backgroundmenucard");
This way you don't remove and add the class to the specific (this) element
I am not very sure with the use of "this" [current context] in jquery.What I know is- it prevents the dom from searching all the elements, it just work on that current element, which improve performance[correct me if I am wrong].Also I am not sure when to use this and when not.
lets say, should I go for
$("span",this).slice(5).css("display", "none")
or
$("span").slice(5).css("display", "none")
both will work, but I am not very clear as how really it works.can somebody explain it with a diff/proper example, and when to use what?
[EDIT]
$(function() {
$("#clickme").click(function() {
$("span",this).slice(5).css('display', 'block');//doesn't work ? why?
$("span").slice(5).css('display', 'block');//works..why?
});
});
enter code here <span id="clickme">Click me</span>
<span>itam1</sapn>
<span>itam2</sapn>
<span>itam3</sapn>
<span>itam4</sapn>
<span>itam5</sapn>
...upto10
Usually you can use the this keyword on event handlers since it will be a reference to the element that triggered the event and other jQuery functions like $.each.
For example when handling a click event lets say:
$('.parentElement').click(function () {
$('.foo', this).hide();
});
The above code, will hide all the elements with class foo that are descendants of the currently parentElement that was clicked.
The use of the context argument of the jQuery function is the equivalent of making a call to the find method:
$(expr, context);
// is just equivalent to:
$(content).find(expr);
EDIT: Looking at your example:
$("#clickme").click(function() {
$("span",this);//... (1)
$("span");//.. (2)
});
The first line, will look for all the span elements that are inside of #clickme (its descendants), since that element was the one that triggered the click event.
The second line, will look for all the span elements on the whole page.
How it works
Lets use this HTML for the examples:
<div id="container">
<div class="column">Link 1</div>
<div class="column">Link 2</div>
</div>
<div id="footer">
Link 3Link 3
</div>
The scoping parameter of the jQuery function should only be used if you already have a cached reference to a DOM element or jQuery wrapped element set:
var $set = $('#container');
$('a', $set).hide(); // Hides all 'a' tag descendants of #container
Or in an event:
$("#container").click(function(e){
$('a', this).hide(); // Same as call above
}
But it makes no sense to use it like this:
$('a', '#container').hide()
When it should be written like this:
$('#container a').hide();
Having said all that, it is generally cleaner and clearer to just use .find() instead of using the second parameter in the jQuery function if you already have the jQuery or DOM element. The first example I gave would be written this way instead:
var $set = $('#container');
$set.find('a').hide(); // Hides all 'a' tag descendants of #container
If this one call was the only reason you grabbed the #container object, you could also write it this way since it will still scope the search to the #container element:
$("#container a").hide(); // This is the same as $('a', "#container");
Why would you scope your selections
When jQuery looks for an unscoped selector, it will search through the entire document. Depending on the complexity of the selector, this could require a lot of searching. If you know that the element you are looking for only occurs within a specific parent, it will really speed up your code to scope the selection to that parent.
Regardless of what method of scoping you choose, you should always scope your selectors whenever possible.