jQuery event delegation with non-trivial HTML markup? - javascript

I have two DIVs, first one has a link in it that contains the id of the second DIV in its HREF attribute (complete setup on jsbin).
I want to animate the second DIV when user clicks on the first - anywhere on the first. I also want to use event delegation because I'll have many such "DIV couples" on a page (and I'm using a JS snippet from this SO answer).
If user clicks on the DIV itself, it will work - just check firebug console output. The problem is, if user clicks on one of the SPANs or the link itself, it doesn't work anymore.
How can I generalize the click handler to manage clicks on anything inside my DIV?
Here's the JS:
$('#a').click(function(e) {
var n = $(e.target)[0];
console.log(n);
if ( n && (n.nodeName.toUpperCase() == 'DIV') ) {
var id = $(n).find('a').attr('hash');
console.log(id);
$(id).slideToggle();
}
return false;
});

It took me so long to write up the question that I decided to post it anyway, perhaps someone suggests a better way.
Here's the solution I found (jsbin sandbox):
$('#a').click(function(e) {
var n = $(e.target)[0];
var name = n.nodeName.toLowerCase() + '.' + n.className.toLowerCase();
if (n && (name === "div.clicker" || $(n).parents("div.clicker").length )) {
var id = $(n).find('a').attr('hash');
if(!id) {
id = $(n).parents("div.clicker").find('a').attr('hash');
}
console.log(id);
}
return false;
});

Here is my solution. Not sure if that's exactly what you wanted, but it works for me.
$(document).ready(function() {
$('#a').click(function() {
var a = $("a", this);
$(a[0].hash).slideToggle();
});
});
Edit: Tested in both IE7 and Fx3
Edit: In that case...
$(function() {
$("a.tab").click(function() {
$($(this).attr("hash")).slideToggle();
return false;
});
});
Something like that might work (putting the tab class on anything that has a div "attached" to it). However, I'm not sure unless I actually see an example of it. Although if you want it to work when clicking on the span...you would attach the class to the span, and instead do:
$(function() {
$("span.tab").click(function() {
var a = $("a",this);
$(a.attr("hash")).slideToggle();
});
});
Not sure if you want an open div to close if another one is opened. If this doesn't solve your problem, it would help if you would put up an example on jsbin...

Related

Jquery remove item from localstorage if the link is already clicked

I have a page with a few filters for search results. These filters are links and upon clicking, I am adding the id to localstorage. When the page reloads it looks in the localstorage if the id of the link exists, it modifies the css of that particular link. I am able to achieve this.
Now, when the same link is clicked again, I need to be able to remove the id of the link from localstoarage so it does not change the css when the page reloads.
Before Clicking
After clicking
Here is my code. Some kind people from StackOverflow helped me get this piece of code together. I need it to extend. Please let me know if any of this doesn't make sense. Would gladly rewrite my sentences.
$(document).ready(function() {
//localStorage.clear();
var cached = localStorage.getItem('filters');
var filters = (cached) ? JSON.parse(cached) : {};
for (id in filters) {
$('#' + id).addClass('li-checked');
}
$('.li-filter').click(function(e) {
//event.preventDefault();
$(e.target).addClass('li-checked');
$(e.target).removeClass('li-unchecked');
var id = $(e.target).attr('id').toString();
if (filters[id]) {
filters[id] += 1;
//filters = $.grep(filters, function(e) { return e.id!=id });
} else {
filters[id] = 1;
}
console.log(JSON.stringify(filters));
localStorage.setItem('filters', JSON.stringify(filters));
});
});
#Rohit If I understood your question correctly, onclick you have to add a class and remove it in case it is already clicked.
For this scenario, I will suggest writing functionality to toggle classes that can help.
You need to get class on the element while li is clicked.
if($(e.target).attr("class").contains("li-checked"))
{
$(e.target).removeClass('li-checked');
$(e.target).addClass('li-unchecked');
}
else if((e.target).attr("class").contains("li-unchecked"))
{
$(e.target).removeClass('li-unchecked');
$(e.target).addClass('li-checked');
}
If the class is 'li-checked' then remove it and add 'li-unchecked'
and if the class is 'li-unchecked' then remove it and add 'li-checked'
I hope it helps.

Access a JQuery object in multiple event handlers

I am new to JQuery and web development in general and hence facing a troubling issue during development. My website interface looks like below:
When a user clicks on any checkbox (for referral purposes, I have selected the box above 'chk2') and then clicks on the 'Show Evidence' button (box 2 in the image), I want the user to be able to highlight portions of the article displayed in the adjacent iframe. I am using a text highlighter Jquery plugin I found on the web. The code for the click event of the 'Show Evidence' button looks like:
$('.show_evidence').click(function(event){
var iframe = document.getElementById('myiFrame');
var hltr = new TextHighlighter(iframe.contentDocument.body);
hltr.setColor("yellow");
});
The above code works fine.
Now, I want to set the highlight color (i.e. hltr.setColor("blue")) to blue when the user clicks on the checkbox 'Unselect' (box 3 in the image). For that I need to be able to access the 'hltr' object I have defined above (i.e. inside the 'click' event handler for '.show_evidence'). Also I want to set the highlight color back to 'yellow' when the user unchecks the 'Unselect' checkbox.
$(".unselect").change(function() {
if(this.checked) {
//Something like - hltr.setColor("blue");
}
else {
// Something like - hltr.setColor("yellow");
}
});
Finally, I also want to unset or undefine the object 'hltr' when the user clicks on the link 'Hide Datums' (box 1 in the image).
So my question is how do I access the hltr object inside the event handlers for .Unselect and the 'Hide Datums' link.
After a lot of stackoverflow surfing, I found that I could use external variables but I am not sure whether that will work for objects. Also, is there a universally recommended design that I should use or follow? What is the best way to achieve what I want?
Looking forward to your suggestions. Please help!
Regards,
Saswati
One way you can go about is extract the lines of code which does the element selection to a separate method.
var hltr;
function getBodyElementOfIframe() {
var iframe = document.getElementById('myiFrame');
if(!hltr) {
hltr = new TextHighlighter(iframe.contentDocument.body);
}
return hltr;
}
Call that method where you want to access the element and then set the color.
$(".unselect").change(function() {
var hltr = getBodyElementOfIframe();
if(this.checked) {
hltr.setColor("blue");
}
else {
hltr.setColor("yellow");
}
});
There are a number of ways to solve it, but one simple way to do it would be to move the variable outside the scope of the method so that it is accessible through out.
I would put it inside on ready.
$(document).ready(function () {
var hltr = {};
$('.show_evidence').click(function(event){
var iframe = document.getElementById('myiFrame');
hltr = new TextHighlighter(iframe.contentDocument.body);
hltr.setColor("yellow");
});
$(".unselect").change(function() {
if(this.checked) {
//Something like - hltr.setColor("blue");
}
else {
// Something like - hltr.setColor("yellow");
}
});
})();
If you need the variable inside several functions, declare it outside of all of them.
$(function(){
var iframe = document.getElementById('myiFrame');
var hltr = new TextHighlighter(iframe.contentDocument.body);
$('.show_evidence').click(function(event){
hltr.setColor("yellow");
});
});
When your event handlers fire, they'll be updating this var and it will be accessible to the next handler called.

Click function on dynamic created elements that shows content of clicked element

I have created a search page that has on top and bottom page navigation. This is a simplified version just to show my problem:
$(function(){
$("select").on("click" , function(){
if ($(this).val() == 10){
var total_pages = 3;
}
else{
var total_pages = 5;
}
var pages = "";
for (var i = 1; i <= total_pages; i++) {
var pages = pages + "<span class='page'>" + i + "</span>";
}
$(".pagination").html(pages);
});
$("select").click();
$(".page").on("click" , function(){
alert($(this).text());
});
});
And you can try it yourself here:
JSFiddle
In this example you have 5 pages when you select 10 items per page and 3 pages for 20 results per page. But when you select an other value per page the alert function doesn't show up anymore. It seems that changes, after the document is loaded, are not being noticed by the document.ready function.
I searched and saw a lot of familiar questions and their solutions. I can imagine also some alternatives but not the one who can satisfy me. Because unfortunately I don't know now a simple solution without a lot of extra code with id's, classes, data-attributes etc. Who can contribute me some knowledge for a good solution?
That's because when you do this:
$(".pagination").html(pages);
You remove old DOM elements and it's "click" functions and replace it with new HTML.
Add this:
$(".page").on("click" , function(){
alert($(this).text());
});
right after first line I mentioned
Event delegation is a good practice when you deal with dynamic content. In your case, you can attach the click event to the parent.
I just tweaked one line in your original code:
$(".pagination").on("click" , ".page" , function(){
alert($(this).text());
});
Live demo: http://jsfiddle.net/zkDF4/6/

Removing Yet not Deleting an Element/Variable from an HTML Document

I am testing out with a different way of menus. My code is the following:
JavaScript
var hubOpen = 0;
var test = "test";
$(document).ready(function(){
$('#hub').click(function(){
if(hubOpen == 0){
$('#hub').append(test);
hubOpen = 1;
} else {
//code for taking "test" out here
hubOpen = 0;
};
});
});
HTML
<body>
<p id="hub">Hub</p>
</body>
If you'd like, here's a jsFiddle here. The code is to make sure that when the id "hub" is clicked, "test" appears. When hub is clicked again, "test" disappears. The code, when run, opens test, doesn't let you click it again, but it doesn't delete "test" (as there is no code for it).
My question: How would I delete the variable "test" from the document but not to delete the variable forever, as I would need to use it later? Would the jQuery method
.replace();
work?
Thanks in advance!
To remove all the contents of an element, jQuery offers .empty():
$('#hub').empty();
The variable is completely separate from the element, so no problems there. If you wanted to restore the original text, just use .text():
$('#hub').text('Hub');
Updated fiddle
You can do something like this:
$(document).ready(function(){
$('#hub').click(function(){
$(this).text() == 'Hub' ? $(this).html('text') : $(this).html('Hub');
});
});
On click if the text is 'Hub' change it to 'text' else change it again to 'Hub'.

How can I get this Javascript to work with multiple links on the same page?

As of right now, I am able to get this javascript function to work with one link.
However, I would like to use the function with multiple links. I have changed obj to different values and have also tried using more than one function specified with different values for each to get a working prototype, but nothing seems to work. Below is the javascript function from scratch, no changes.
<script type="text/javascript">
function gObj(obj) {
var theObj;
if(document.all){
if(typeof obj=="string") {
return document.all(obj);
}
else {
return obj.style;
}
}
if(document.getElementById) {
if(typeof obj=="string") {
return document.getElementById(obj);
}
else {
return obj.style;
}
}
return null;
}
</script>
And the link code:
<div id="axphsh">
<b>Phone:</b><a href="#" onClick="
gObj('axphsh').style.display='none';
gObj('axphhd').style.display='block';
return false;">Click for Phone Number</a></div>
<div id="axphhd" style="display:none">
<b>Phone:</b> 555-555-5555</div>
Ultimately what I want is to use the link code for multiple numbers on the same page, all hidden by default, then unhidden onClick. But like I said, this only works for one phone number link, then if there are more specified on the same page, the onClick event doesn't work at all. I am thinking it has to do with getElementById since div ids for links can be specified in that manner, but I am not completely sure.
You should learn some basic JS DOM manipulation.
Why do you even use document.all which is not a part of the standard? Use document.getElementyById or document.querySelector.
If all of your boxes with phone numbers were similar you could go with a more general function:
HTML:
<div class="phone-link-container">
Click for phone number
</div>
<div class="phone-number-container">555-555-555</div>
JS:
function showNumber (e) {
var link = e.target,
link_container = e.target.parentNode,
phone_number = link_container.nextElementSibling;
link_container.style.display = 'none';
phone_number.style.display = 'block';
}
var numbers = document.querySelectorAll('.show-phone-number');
Array.prototype.forEach.call(numbers, function (el, i) {
el.addEventListener('click', showNumber);
});
It selects all elements with a class show-phone-number and binds a function showNumber to the click event for each of them. This function hides parent of the link (which would be phone-link-container in my example) and shows next sibling of the parent (which is phone-link-container).
http://jsfiddle.net/9ZF9q/2/
In case your JavaScript code is in head you need to wrap all DOM manipulations inside window load callback:
window.addEventListener("load", function () {
var numbers = document.querySelectorAll('.show-phone-number')
Array.prototype.forEach.call(numbers, function (el, i) {
el.addEventListener('click', showNumber);
});
});
You can read more on functions used there on https://developer.mozilla.org/en-US/docs/DOM/Element.nextElementSibling
When it comes to DOM manipulation if you want to keep the compatibility with all older browsers it's easier to use jQuery library - especially if you're a beginner.

Categories

Resources