I need your support in selecting a logic. My script pulls few rows from DB and displays on screen. I want to give a feedback form for each data. These feedback forms must be hidden and should be visible on click of a text (Like reply to this) - Pls refer the picture.
I have tried with the help of java script and CSS
<script>
function hideElement()
{
document.getElementById("target").style.display="none";
}
function showElement()
{
document.getElementById("target").style.display="block";
}
</script>
// Data-1 fetched from DB goes here
<a href='#target'>Reply to this post</a>
<span id='target' style='display:none'>
// Feedback form for Data 1 here
</span>
// Data-2 fetched from DB goes here
<a href='#target'>Reply to this post</a>
<span id='target' style='display:none'>
// Feedback form for Data 2 here
</span>
But it works only for the first record, - Javascript finds the first object with the name "target" and sets display property 'none' or 'block'
What logic can I use to generate dynamic ID assigned to each record and make java sript to run for that. Are there any other better logics to do this? (I'm sure ther are)
Element IDs must be unique, but any time you find yourself generating unique IDs for repeating elements there's probably a simpler, more generic way to implement whatever you're doing.
For this type of function you don't need IDs at all, you can use classes and DOM navigation to get from the clicked item to the related span, using a single delegated event handler as follows:
// bind click handler to the document
document.addEventListener("click", function(e) {
// test if the actual clicked item has the class "reply"
if (e.target.className.match(/\breply\b/)) {
e.preventDefault();
// find the related target span
var target = e.target.parentNode.querySelector(".target");
// update its visibility
target.style.display = target.style.display === "none" ? "block" : "none";
}
});
<div> <!-- note added wrapper div -->
Reply to this post
<span class='target' style='display:none'>
Feedback form for Data 1 here
</span>
</div>
<div> <!-- note added wrapper div -->
Reply to this post
<span class='target' style='display:none'>
Feedback form for Data 2 here
</span>
</div>
I've put some comments within the above JS to explain what it's doing, but the important line is this one:
var target = e.target.parentNode.querySelector(".target");
Within the event listener, the e argument is the event object which holds various bits of information about the event being handled. e.target tells which element was clicked. Having already tested that element's class to see if it was one of the "reply" anchors, we then use the .parentNode property to get a reference to the wrapper div that I added to your markup, then from there .querySelector(".target") finds a descendant of the div that has the class target.
As you can see I've modified your html to support the above as follows:
Change the span ids to be classes
Given the anchors class="reply"
Added wrapper div elements for each group, to make the DOM navigation simple and reliable. You could navigate from the anchor to the span using e.target.nextSibling, except then you'd have to add extra code to skip over any text nodes. I find a wrapper element easier to work with. Of course, if your elements are already in some kind of wrappers for other purposes then you can just use the existing wrappers.
Note: it would be good to remove the inline styles, and to show and hide the spans by adding and removing classes rather than directly updating their styles, but that's not really the main issue here so I'll leave that as an exercise for the reader.
http://codepen.io/sheriffderek/pen/BzmAwg
Step 1: ditch those IDs
markup
<ul class="item-list">
<li>
<p>default stuff</p>
<div class="hidden-thing">
hidden stuff
</div>
</li>
<li>
<p>default stuff</p>
<div class="hidden-thing">
hidden stuff
</div>
</li>
<li>
<p>default stuff</p>
<div class="hidden-thing">
hidden stuff
</div>
</li>
</ul>
Step 2: hide the hidden stuff in CSS and not inline
styles
(this is stylus syntax.. but same point)
.item-list
list-style: none
margin: 0
padding: 0
li
background: gray
padding: .5rem
margin-bottom: 1rem
cursor: pointer
.hidden-thing
display: none // hide it
Step 3: get the thing you want to click with jquery or with vanilla JavaScript - attach event handler - use this to note what element the event happens on - with that element... traverse down the DOM and find the thing you want - then use the show method, whichs sets display: block... or fadeIn() or animate() or whatever you like.
JavaScript
$('.item-list li').on('click', function() {
$(this).find('.hidden-thing').show();
});
// or...
$('.item-list li').on('click', function() {
$('.item-list li').find('.hidden-thing').hide();
$(this).find('.hidden-thing').show();
});
If you want only one hidden thing open at a time, you can hide all the items each time first - which is kinda janky, but usual.
BUT... there is a better way to do this, where you add an active class to the whole item. This lets you style things inside it and just generally gives you a larger scope to work with. Here is an example of that. : ) It uses .closest - and passes the event into the click handler to stop the outer click action from bubbling up: http://codepen.io/sheriffderek/pen/oLoqEy
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've used this exact code on a different div element and it works perfectly. When I went to add the same code to another div element with a different id it registers the element has been clicked but it doesn't add or remove any of the classes.
$('#quoteClick').click(function(){
$('#cbox-1').addClass('displayCboxBackground');
$('#cbox-2').removeClass('displayCboxBackground');
$('#cbox-3').removeClass('displayCboxBackground');
$('#dbox-1').addClass('displayBlock');
$('#dbox-2').removeClass('displayBlock');
$('#dbox-3').removeClass('displayBlock');
console.log("clicked");
});
The html structure is as follows:
<div id="cbox-1">
<div id="dbox-1">
content...
</div>
</div>
<div id="cbox-2">
<div id="dbox-2">
content...
</div>
</div>
<div id="cbox-3">
<div id="dbox-3">
<div id="quoteClick">
a quote
</div>
</div>
</div>
JSFiddle: https://jsfiddle.net/m81c23cx/1/
In the fiddle you can see the content will changes when each header is clicked. When the "quoteClick" element is clicked I want it to change to the second headers content exactly how it does when the second header is clicked.
I can see in Chrome's console that when I click the div element that it highlights all the classes but it doesn't change any of them. I have the jQuery inside a document.ready() function so it should be waiting for the DOM to load and it works perfectly when I just write the lines into the console.
I'm surprised that nobody actually questioned your use of ids (instead of suggesting that you should double-check for dupes). The reason why this code is hard to debug is because it's too complicated. As a result, you'll have a hard time fixing issues similar to this in the future too.
Drop it, do it better.
I didn't even go through your fiddle. Instead, I'm going to propose that you change your approach altogether.
Update your HTML and use classes instead of ids. Something similar to this:
<div class="cbox">
<div class="dbox">
content...
</div>
</div>
<div class="cbox">
<div class="dbox">
content...
</div>
</div>
<div class="cbox">
<div class="dbox">
<div id="quoteAdvert">
a quote
</div>
</div>
</div>
Update your JavaScript and use this to get the context of the current box:
$('.cbox').click( function cboxClicked () {
// Remove the previous class from all .cbox & .dbox elements; we don't care which
$('.cbox').removeClass('displayCboxBackground')
$('.dbox').removeClass('displayBlock')
// Add a new class to the clicked .cbox & it's child .dbox
$(this).addClass('displayCboxBackground')
$(this).children('.dbox').addClass('displayBlock')
})
The beauty of this? You can have 1000 boxes, it'll still work. No need to add any extra lines of code.
Here's a fiddle showing it in action.
The example code you provided is not consistent with the jsfiddle you created.
In your fiddle, you use the jquery selector $('#quoteClick') but there is no element with that id. There is a #quoteAdvert element however. Change that and you'll see the click in the console.
The classList property returns a token list of the class attribute of the element in question. Luckily for us, it also comes with a few handy methods:
add - adds a class
remove - removes a class
toggle - toggles a class
contains - checks if a class exists
// adds class "foo" to el
el.classList.add("foo");
// removes class "bar" from el
el.classList.remove("bar");
// toggles the class "foo"
el.classList.toggle("foo");
// outputs "true" to console if el contains "foo", "false" if not
console.log( el.classList.contains("foo") );
// add multiple classes to el
el.classList.add( "foo", "bar" );
I have a list of divs in the following structure, where the text in the a.status-progress will either say "in progress" or "not started":
<div class="plan-section">
<div class="tableView-row">
<p class="plan-name">
<a>some name</a>
</p>
<a class="status-progress">in progress</a>
</div>
</div>
<!-- same structure as above but not expanded -->
<div class="plan-section"></div>
<div class="plan-section"></div>
All the <a> tags with in each <div> act as links. What I would like to do is loop through each div, check and see if the a.progress has the string "in progress" with in it . If it doesn't I want to remove the cursor:pointer css property and any events attached to the <a> tags. Current my jQuery implementation is:
// remove linking if plan is not joined
$('.status-progress').each(function(i){
var planLinks = $('.status-progress, .plan-name a');
var planStatus = $(this).text();
if (planStatus === "in progress"){
planLinks.css('cursor','pointer')
}
});
This is not working properly though because I believe my logic with the each() is wrong or that I need to add another one later in the code block. Thanks for the help!
EDIT: Added proper class for status-progress
The line:
var planLinks = $('.status-progress, .plan-name a');
...will select all such anchor elements, not just the ones related to the current iteration of the .each() loop. One way to get just the related ones is:
var planLinks = $(this).closest("div").find("a");
That is, use DOM traversal methods to find the containing div and then select the anchors within it. Or you could go based on siblings, etc., but that is more fragile since a change to the html structure is then more likely to require a change to the JS.
But you don't really need the .each() loop if you do something like this instead:
$("a.status-progress:contains('in progress')") // find the 'in progress' anchors
.closest("div") // get their containing divs
.find("a") // find the anchors in those divs
.off() // remove the event handlers
.css('cursor','pointer'); // set the CSS property
I am in the lookout for a script that can show one DIV at a time and Hide the rest (2 in the example I took)additionally I want the user to navigate back and forth
i.e
Once the user clicks next DIV 1 is displayed so on till DIV3
He should also be able to traverse from DIV2 - DIV1 and so on
I did find this development interesting
http://jsfiddle.net/meetrk85/Y7mfF/
Thanks a billion in advance.....
Given the following HTML:
<div class="sample">div1</div>
<div class="sample">div2</div>
<div class="sample">div3</div>
next
prev
The following jQuery seems to achieve your requirements:
// selects all the divs of class='sample',hides them, finds the first, and shows it
$('div.sample').hide().first().show();
// binds a click event-handler to a elements whose class='display'
$('a.display').on('click', function(e) {
// prevents the default action of the link
e.preventDefault();
// assigns the currently visible div.sample element to a variable
var that = $('div.sample:visible'),
// assigns the text of the clicked-link to a variable for comparison purposes
t = $(this).text();
// checks if it was the 'next' link, and ensures there's a div to show after the currently-shown one
if (t == 'next' && that.next('div.sample').length > 0) {
// hides all the div.sample elements
$('div.sample').hide();
// shows the 'next'
that.next('div.sample').show()
}
// exactly the same as above, but checking that it's the 'prev' link
// and that there's a div 'before' the currently-shown element.
else if (t == 'prev' && that.prev('div.sample').length > 0) {
$('div.sample').hide();
that.hide().prev('div.sample').show()
}
});
JS Fiddle demo.
References:
first().
hide().
next().
on().
prev().
show().
text().
:visible selector.
Addenda:
A quick explanation of why I changed the html in the linked demo:
<div name="sample">div1</div>
<div name="sample">div2</div>
<div name="sample">div3</div>
<a href="#" id="display" value="display">next</div>
<a href="#" id="display1" value="display">prev</div>
The name attribute, in a div serves no purpose. Certainly not if all the elements are sharing the same name (they're not input elements, they're linked-to by an a, so use a class name).
The value attribute has no association with an a element, and serves, so far as I can tell, no purpose. For this, in the script above, I chose, again, to use a class name, since the same 'value' of the attribute was shared, though a data-* attribute could have been used, and would have been valid.
The closing </div> tags weren't closing anything, so they were changed to </a>.
Hello I have some HTML that looks like this,
<div id="music_interests">
<ul class="interests">
<li >
<div class="interest inline">
<img src=""/>
<div class="interest_popup">
1 users have this interest.
Remove interest </div>
</div>
</li>
</ul>
When users clicks the remove button I need to select the parent div (in this case music_interests). How would I go about that?
I have tried doing the following,
$(this).parent().parent().parent().parent() but is there a more elegant way?
To complicate things futher I will not actually no the parents ID when in the app as the remove button occurs in 4 or 5 different areas on the page.
you should use closest()
$(this).closest('div#music_interests');
//find the nearest div with id "music_interests"
//if i omitted the id, it retrieves the div with class "interest_popup"
or parents()
$(this).parents('div:eq(1)');
//get ALL the ancestor divs (until it reaches root tag)
//since music_interests is just 2 levels up, use :eq(1)
If the ID of the DIV you want to remove is static you should only use the ID selector (not something like $("div#music_interests")) as the ID selector is directly mapped to the DOM function document.getElementsById which is pretty fast:
$("#music_interests").remove();
If the ID isn't static you could get the UL just like that:
$(function(){ //execute when page has been loaded
$(".remove").click(function(){ //attach click handler
var removeDiv = $(this).closest("ul").parent().remove(); //get next UL -> DIV is its parent
return false; //stop further processing of "click" event
});
});
if remove button always exist in ul tag (in all your 4 or 5 different areas) then you can use the following code.
$(this).closest("ul").parent()
in this case u don't even need to give id to DIV tags