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.
Related
I know this is awfully simple, but I'm new to this and I just need to be shown it once. So when I'm using jQuery/javascript I find myself writing repetitive code again and again to target different elements with the same function, for example:
$(function() {
$('.icon1').click(function() {
$('.info1').toggle().addClass('animated fadeInDown');
$('.info2, .info3, .info4').hide();
});
});
$(function() {
$('.icon2').click(function() {
$('.info2').toggle().addClass('animated fadeInLeft');
$('.info1, .info3, .info4').hide();
});
});
and this repeats again for icon3 and icon4. I'm selecting a different element, showing another, hiding another three, and adding different classes in each function, and I don't know what would be the best way not to repeat the whole thing for each element. I would be very glad to be shown any ideas to refactor this, and wouldn't mind seeing how that is done in vanilla js also.
(For illustration the code here is a snippet from the code on the experience section of my portfolio where clicking on an icon reveals an info panel about it, and hides any previously shown info panels.)
Use a common class, use this, and not to remove it from the collection
$(function() {
$('.commonClass').click(function() {
$(this).toggle().addClass('animated fadeInDown');
$('.commonClass').not(this).hide();
});
});
You should be able to separate those selectors with commas.
$('.icon1,.icon2').click(function()
Or assign each a single class they share that behavior? ".icon-btn" where you use ".icon-btn" as the selector for any you wish to have that behavior.
It would be better if you can plan your html better with data attribute
for eg:
<div class="icon" data-info = "1"> <div>
<div class="icon" data-info = "2"> <div>
<div class="info-1 info"> <div>
<div class="info-2 info"> <div>
$('.icon').click(function() {
var className = '.info-' + $(this).data('info');
$('.info').hide();
$(className).toggle().addClass('animated fadeInDown');
});
you can also remove .info1,.info2 from js code by adding some common class in html as info to them.
for eg
I'm making some assumptions about your actual HTML, but you could probably leverage the siblings() method in this case.
$(document).on('click', '.icon', function() {
$(this).toggle().addClass('animated fadeIn')
.siblings().hide();
});
This piece of code appears in a js script I have been asked to modify - I'm not sure why it is written in this way, it doesn't make sense to me.
Can anyone help explain what it is doing, and if it can be simplified to be a little more meaningful?
var unformathtml = $(this).text();
if(unformathtml.trim().length>showChar) {
$(this).parent().parent().parent().parent().parent().find('.comment-footer').fadeOut();
}
Lets' pretend we have a DOM like this:
<parent-5>
<target-element>Content</target-element>
<parent-4>
<parent-3>
<parent-2>
<parent-1>
<focused-element>Some Text</focused-element>
</parent-1>
</parent-2>
</parent-3>
</parent-4>
</parent-5>
What this code is saying is "if the text inside of <focused-element> has more characters than showChar then fade out <target-element>.
A better way of doing this would be to give <parent-5> some kind of identifier, which could be an ID or a class, and target that instead of the repeated .parent() call.
Here's an example which showcases the idea:
$('#oldMethod').click(function() {
$(this)
.parent()
.parent()
.parent()
.parent()
.parent()
.find('.comment-footer')
.toggleClass('red');
});
$('#newMethod').click(function() {
$(this)
.closest('.comment-container')
.find('.comment-footer')
.toggleClass('red');
});
.red {
color: #F00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="comment-container">
<div>
<div>
<div>
<div>
<button id="oldMethod">Old</button>
<button id="newMethod">New</button>
</div>
</div>
</div>
</div>
<div class="comment-footer">Footer</div>
</div>
Wow, that really doesn't make much sense. It is doing this:
1) Getting the raw contents out of an element
2) Checking to see if it is longer than a certain length
3) If so, fading out another element on the page
The parents() thing is very error-prone. It is going up a very precise number of levels in the HTML tree and then descending downwards to find an element with a class of '.comment-footer'. As a result a slight rearrangement of either element in the DOM might result in the code no longer working, because it can't find the specified element.
What you want is to find the tag-to-hide more directly. Ideally, the element-to-hide and the element-that-decides-to-hide would be next to eachother in the DOM (i.e. the element being hidden would be a child or sibling of the element that decides whether or not to hide it). This makes it very easy for the one to find the other. If that isn't possible, your next best bet would be to simply assign an id to the element you are trying to hide and then select on that id directly:
var unformathtml = $(this).text();
if(unformathtml.trim().length>showChar) {
$('#to_hide').fadeOut();
}
As a quick aside, .text() is used (instead of .html()), because the former removes any HTML tags. This way you are measuring the amount of "actual" text inside $(this) to determine whether or not you want to hide said element. So that part is probably fine.
I want to use jQuery to work with events in a given search box. My issue is that
I don't know how to build the selector correctly, so that JQuery accepts it.
I think I'm getting confused because I need the second element in the list and need to select that one.
The runtime HTML looks like this: (Adapted from Chrome Developer tools, only the relevant class and IDs are shown. There are no IDs to be shown.)
<body class=km-ios7 km-7 km-m0 km-web km-black-status-bar km-vertical km-widget km-pane>
<div class="km-widget km-view">
<!-- Begin 3rd party control -->
<div class=class="km-widget km-view">
<div km-header>
<div class="km-content km-widget km-scroll-wrapper">
<div class=km-scroll-header>
<div class=km-scroll-container>
<div class="km-listview-wrapper">
<form class="km-filter-form">
<div class="km-filter-wrap">
<input type=search >
What I've tried
Since my event wasn't firing I assume my selector was wrong. I opened chrome developer tools after I did "inspect element". The bottom of the tools listed all the parent tags used for that element (with no class or ID). As a test, I've tried hiding the search box using the following:
$("div").hide(); // hides everything...
$("div div").hide(); // hides the wrong element on the page
$("input").hide(); // nothing
$(":input").hide(); // nothing... saw this example somewhere, don't understand it
$("input:text").hide(); // nothing... saw this example (http://stackoverflow.com/q/17384218/328397), don't understand it
I looked at this W3 document, but didn't see what I was looking for (unless I missed it)
Any assistance in getting the right selector would be appreciated.
In the page you linked it's the second div under defaultHomecontent, so
$("#defaultHomeContent div:nth-child(2)")
You actually want to hide the div with class km-filter-wrap.
A safer alternative may be to not deal with selectors and instead show/hide the wrapper element for the ListViewFilter's searchInput element:
var listView = $("#local-filterable-listview").kendoMobileListView({
...
}).getKendoMobileListView();
listView._filter.searchInput.parent().hide();
or
listView.wrapper.find(".km-filter-wrap").hide();
In general, it's a good idea to use the elements that are exposed by Kendo UI controls as much as possible instead of manually building queries (since they might change in future versions).
You could also extend the ListView widget with your own API method for this:
kendo.mobile.ui.ListView.fn.filterVisible = function(value) {
var wrapper = this._filter.searchInput.parent();
if (value) {
wrapper.show();
} else {
wrapper.hide();
}
};
then you could use
listView.filterVisible(false); // hide the filter
you can use the find function. Let suppose you have input field inside footer div like this.
<div id="footer">
<div>
<div>
<input type="text" name="text" value="Search" />
</div>
</div>
</div>
You can use selector like this $("#footer input").hide() or $("#footer").find("input").hide() or $('input[name=text]', '#footer').hide();
Based on what you have added.
You could use
$("input[type='search']")
as a selector.
See if that helps. Here is an example
You could also combine the selectors in this manner:
var $container = $("div.km-widget");
var $searchBox = $container.find("input[type='search']");
I have a dropdown function that I need to work only on the div clicked, not all (I have 14+ of the same classes on the page that need to be displayed when a certain one is clicked)
At the moment my jQuery is as follows.
$('.qacollapsed').hide();
$('.qa').click(function () {
$('.qacollapsed').slideToggle();
$(this).toggleClass('active');
});
Of course, that is toggling all qacollapsed classes when there is 14 on the page (Q&A)
Is there a way for it to only drop down the one that is clicked?
the HTML
<div class="qa">
<h4 class="question"> </h4>
</div>
<div class="qacollapsed">
<p> </p>
</div>
It would be helpful to provide a snippet of HTML here, but I'll take a guess at the structure of your markup for now..
Instead of referencing all .qacollapsed elements, you need find elements that are close to the .qa that was clicked, e.g.:
$('.qa').click(function () {
$(this) // start with the clicked element
.find('.qacollapsed') // find child .qacollapsed elements only
.slideToggle();
$(this).toggleClass('active');
});
This will work if .qacollapsed is inside .qa - if not, you might need to use next (for siblings), or one of the other jQuery tree traversal methods.
Yo could find() it or use this as a context in the selector to choose only a descendent of the clicked object
$('.qa').click(function () {
$('.qacollapsed', this).slideToggle();
//You could do $(this).find('.qacollapsed').slideToggle();
$(this).toggleClass('active');
});
Check out the jQuery selectors and why not just use $(this)?
$('.qacollapsed').hide();
$('.qa').click(function () {
$(this).toggleClass('active').next().slideToggle();
});
Personally, I'd give all the divs IDs, the clickable bit being the ID of the question in the database for example, and the answer just being id='ID_answer' or something, then use jquery to slide in the div with the id corresponding to the link clicked, ie
Var showIt = $(this).attr('id') + '_answer'
$('.qacollapsed').not('#'+showIt).hide();
$('#'+showIt).slideToggle;
That will hide all the divs without that ID and show the required one.
Dexter's use of .next above looks simpler though, I've not tried that as being relatively new to jquery too.
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.