Issue with selectors & .html() in jquery? - javascript

The function associated with the selector stops working when I replace it's contents using .html(). Since I cannot post my original code I've created an example to show what I mean...
Jquery
$(document).ready(function () {
$("#pg_display span").click(function () {
var pageno = $(this).attr("id");
alert(pageno);
var data = "<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
});
});
HTML
<div id="pg_display">
<span id="page1">1</span>
<span id="page2">2</span>
<span id="page3">3</span>
</div>
Is there any way to fix this??...Thanks

Not sure I understand you completely, but if you're asking why .click() functions aren't working on spans that are added later, you'll need to use .live(),
$("#someSelector span").live("click", function(){
# do stuff to spans currently existing
# and those that will exist in the future
});
This will add functionality to any element currently on the page, and any element that is later created. It keeps you have having to re-attach handlers when new elements are created.

You have to re-bind the event after you replace the HTML, because the original DOM element will have disappeared. To allow this, you have to create a named function instead of an anonymous function:
function pgClick() {
var pageno = $(this).attr("id");
alert(pageno);
var data="<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
$("#pg_display span").click(pgClick);
}
$(document).ready(function(){
$("#pg_display span").click(pgClick);
});

That's to be expected, since the DOM elements that had your click handler attached have been replaced with new ones.
The easiest remedy is to use 1.3's new "live" events.

In your situation, you can use 'Event delegation' concept and get it to work.
Event delegation uses the fact that an event generated on a element will keep bubbling up to its parent unless there are no more parents. So instead of binding click event to span, you will find the click event on your #pg_display div.
$(document).ready(
function()
{
$("#pg_display").click(
function(ev)
{
//As we are binding click event to the DIV, we need to find out the
//'target' which was clicked.
var target = $(ev.target);
//If it's not span, don't do anything.
if(!target.is('span'))
return;
alert('page #' + ev.target.id);
var data="<span id='page1'>1</span><span id='page2'>2</span><span id='page3'>3</span>";
$("#pg_display").html(data);
}
);
}
);
Working demo: http://jsbin.com/imuye
Code: http://jsbin.com/imuye/edit
The above code has additional advantage that instead of binding 3 event handlers, it only binds one.

Use the $("#pg_display span").live('click', function....) method instead of .click. Live (available in JQuery 1.3.2) will bind to existing and FUTURE matches whereas the click (as well as .bind) function is only being bound to existing objects and not any new ones. You'll also need (maybe?) to separate the data from the function or you will always add new span tags on each click.
http://docs.jquery.com/Events/live#typefn

Related

jQuery two different ids trigger the same function

There are 2 ids #firstTableTotal, #secondTableTotal and 1 function contentchanged. How to make both ids(#firstTableTotal, #secondTableTotal) use the same function(contentchanged). I tried with the following codes but the result is not as expected.
$('#firstTableTotal').trigger('contentchanged');
$('#secondTableTotal').trigger('contentchanged');
$(document).on('contentchanged', '#firstTableTotal #secondTableTotal', function()
{alert("Calculations go here");
});
As mentioned already you are trigerring the event even before binding it... Also another problem is with your selectors... There must be a comma between each ID.. Else the meaning would be a parent child combination.
It should be
'#firstTableTotal, #secondTableTotal'
Right now what you have actually means select the element with ID secondTableTotal which is the child of a element with ID firstTableTotal.. Which is not the case in your code.
Your aim is to target both the elements. So place a comma between them. This makes the selector choose two different elements.
That is because you are triggering the event even before it is bound.
Also use a comma to separate the 2 different selectors
$(document).on('contentchanged', '#firstTableTotal, #secondTableTotal',
If contentchanged is an event:
var myFunc = function(){
alert("Calculations go here");
};
$('#firstTableTotal').on('contentchanged', myFunc);
$('#secondTableTotal').on('contentchanged', myFunc);
//Some time later
$('#firstTableTotal').trigger('contentchanged');
$('#secondTableTotal').trigger('contentchanged');
If contentchanged is in fact a function:
var contentchanged = function(){
alert("Calculations go here");
};
$('#firstTableTotal').on('some_event', contentchanged);
$('#secondTableTotal').on('some_event', contentchanged);
//Some time later
$('#firstTableTotal').trigger('some_event');
$('#secondTableTotal').trigger('some_event');
if you have a function called contentChanged:
var contentchanged= function () { //do something}
then you can simply add a listener to each DOM node.
$('#firstTableTotal').on(eventNameHere, contentchanged);
$('#secondTableTotal').on(eventNameHere, contentchanged);
It is best to attach the listeners to the node directly, that way when the nodes are removed from the DOM, the listeners will also be garbage collected. If you add the listener to the window, like you are currently doing, you will need to manually remove it in order for garbage collection to occur.

why doesn't my jquery click function work after changing element values?

So I'm making a small quiz app with object oriented JS using Object.create cloning method. I have an ol, and a function called showVals() that populates it with lis. That seems to be working fine. What I'm having trouble with is: my li click function to give the attr of ".selected' class seems to work intitially, but after I click to proceed and qn2.showVals() is called it is no longer giving the lis a class of selected when clicked.
The data for qn2 is there. Everything looks normal, except for the click function no longer working (giving the lis the class).
$(document).ready(function(){
qn1.showVals();
qn1.setAns(1); // calling question1 answer for now
$('li').click(function(){
$('li').removeAttr("class");
$(this).attr({"class": "selected"});
});
$('.proceed').click(function(e){
e.preventDefault();
if ($('.selected').html() == qn1.ctAns) {
if (confirm("You are correct")){
qn2.showVals();
qn2.setAns(3);
};
};
});
});
var qn1 = {
title:"The Mouth of Sauron",
qn: "How did 'The mouth of Sauron' meet his demise?",
img: "images/mouth-sauron.gif",
ans: ["Shot in the back", "Beheaded", "Drowned in a swamp", "Sacrificed"],
setAns: function(x) {
this.ctAns = this.ans[x]; //setting correct answer
},
showVals: function(){
$('#slide-title').text(this.title);
$('.question-box > p').text(this.qn);
$('#obj-img').attr("src", this.img);
$('ol').html('<li>'+this.ans[0]+'</li>'+'<li>'+this.ans[1]+'</li>'+
'<li>'+this.ans[2]+'</li>'+'<li>'+this.ans[3]+'</li>')
}
}
var qn2 = Object.create(qn1);
qn2.title = "Golemn";
qn2.qn = "What this dude's name?";
qn2.ans= ["Golemn", "Gimli", "Goober", "Poop"]
qn2.img = "images/golemn.gif";
This is likely because your li elements are dynamically added.
You should try using jQuery on(), which allows you to bind an event handler to the parent element which must already exists in your DOM, and then you can specify the child/descendant selector that will call the event handler. This child element may still be non-existent at the time you do the event binding. In such a case, you call on() like:
$('ol').on('click', 'li', function () {...});
where ol already exists.
Alternatively, you could always bind your click handler to your dynamically generated li elements after you have added them to your DOM. Although I think that is more processor-time consuming as I assume you have to do this for all quiz questions you ask your user.

Two Html select drop down apply class for span element using Javascript

I am working on HTML select Dropdown. I have two dropdowns one is for font size adjust and other is for text alignment.
When I select the fontsize from the dropdown it has to apply along with text-capitalize (bootstrap css) and If I select the font alignment all three should apply for the span element. For Example.
<div>
<span id="Title"class="text-capitalize">check</span>
</div>
Right now the code was like this
function changeFont_size () {
var select = document.getElementById('font_size');
// Bind onchange event
select.onchange = function() {
document.querySelector("#Title").className = this.value += " text-
capitalize";
};
}
function changeAlignment () {
var select = document.getElementById('text_align');
// Bind onchange event
select.onchange = function() {
document.querySelector("#Title").className = this.value;
};
}
Actually I am newbe on Javascript. Some how I am not getting.
The output result would be the combination of :
<span class="h1 left text-capitalize">Text</span>
Everything should be in pure javascript.
Thanks in advance. Kindly help me.
Here is the Link
This jsfiddle makes your code work. You need to run the code when the document is loaded, so that your onchange functions are being hooked in time.
It does not work exactly like you intended though. Your alignment classes need to be on the parent element and when you select your alignment, you disregard the previously set h1 or h2 class.
window.onload = function() {
var font_size = document.querySelector('#font_size');
// Bind onchange event
font_size.onchange = function() {
document.querySelector("#Title").className = this.options[this.selectedIndex].value += " text-capitalize";
};
var text_align = document.querySelector('#text_align');
// Bind onchange event
text_align.onchange = function() {
document.querySelector("#Title").className = this.options[this.selectedIndex].value;
};
};
You are mixing things up. There are two ways to bind events (well, two ways which are still common even with recent browsers).
The first one is to put a function call in the onsomething property of an element in the html code. Whatever is put there will be executed when the event happens.
<button onclick="alert('hi');">Click me</button>
You should pass the event object to an event handler instead of writing inline code.
<button id="helloworld" onclick="helloworld_onclick(event)">Run</button>
...
function helloworld_onclick(e) {
alert("Hello world!");
}
If you want to be able to bind events dynamically, if you want to bind multiple events to an object and if you want to keep the JavaScript outside of your HTML, the modern way to to so is with addEventListener.
document.querySelector("#helloworld").addEventListener("click", function(e) {
alert("Hello world!");
});
The event object passed (called e in my functions) contains information about what triggered the event and can be used to prevent default behavior and to control event propagation. You can't use "this" in event handlers, but the element which called the handler will be stored in e.target.
In your code, you created functions which, when called, bind events to the elements. Then you bound those functions to the elements with the html attributes.
Finally, you seem to be stuck between querySelector and getElementById. Note that querySelector(All) returns a static node/nodelist while getElement(s)By(...) returns a live node/nodelist. A static node is a copy of all the information about the element. A live node is a reference to the real element. If you modify the element, it modifies the live node, but the static node will keep the old information. You should use getElementById over querySelector for that, and because it runs faster. For code simplicity however, you might prefer always using querySelector. Just don't mix using querySelector("#something") on a line and getElementById("something") on another one, it's the best way to get confused and end up wasting time on a bug because you wrote querySelector("something") or getElementById("#something") instead.
function changeFont_size (element) {
if(element.options[element.selectedIndex].value != 'select'){
document.getElementById('Title').className = element.options[element.selectedIndex].value;
} else{
document.getElementById('Title').className = '' }
}
function changeAlignment (element) {
if(element.options[element.selectedIndex].value != 'select'){
document.getElementById('container').className = element.options[element.selectedIndex].value;
} else{
document.getElementById('container').className = '' }
}
Try this, Hope it will work

How to clone an element and not fire events in Chrome?

I am attempting to clone an input element of type="file". In this input element there is an onchange event. The clone method is called in the function that is called from the onchange event.
The problem I am having is that when I clone the file upload input, the onchange event is fired again. This only happens in Chrome, but works as intended in IE.
<input type="file" id="fileUpLoad" onchange="doFunction();" />
doFunction = function() {
var oldEl = $('#fileUpLoad');
var newEl = $(oldEl).clone();
$(oldEl).attr('id','fileUpLoadOLD');
$(oldEl).before(newEl);
}
I have created a fiddle duplicating this issue. Here is the fiddle.
How can I clone this file upload input without firing the onchange event again and again? I am using jQuery 1.7.1. This is actually a simplified a snippet from ajaxfileuploader.js:
var oldElement = jQuery('#' + fileElementId);
var newElement = jQuery(oldElement).clone();
jQuery(oldElement).attr('id', fileId);
jQuery(oldElement).before(newElement);
jQuery(oldElement).appendTo(form);
UPDATE: fiddle I think this is accomplishing what I need based on #MichaelAngstadt answer. Still testing though.
This is what seems to be working for me in the ajaxfileuploader.js extension:
var oldElement = jQuery('#' + fileElementId);
var oldElEvents = oldElement.data("events");
var newElement = jQuery(oldElement).attr('id', fileId).data("events", null).clone();
jQuery(oldElement).attr('id', fileElementId).before(newElement);
jQuery(oldElement).data("events", oldElEvents);
jQuery(newElement).appendTo(form);
You could always hold the events in a temporary object and re-attach them after cloning like:
doFunction = function(){
var oldEl = $('#fileUpLoad');
var oldElEvents = oldEl.data("events");
var newEl = $(oldEl).data("events", null).clone();
$(oldEl).attr('id','fileUpLoadOLD');
$(oldEl).before(newEl.data("events",oldElEvents));
}
This way oldEl doesn't actually have events defined to be cloned. Otherwise you'll be battling some fundamental functionality with regards to how cloning & the DOM work.
Have you tried cloning the object using $.extend instead.
var newEl = $.extend({},oldEl);
It seems the problem is related to onchange attribute. You can remove/reset the attribute before cloning. After cloning, the change event is triggered again and it results in an infinite loop.
var newEl = oldEl.removeAttr('onchange').clone();
You are loading jQuery, why not using it's methods? If you are using onchange attribute for keeping the event handler, you can use event delegation technique instead.
$(document).on('change', '.fileUpLoads', function() {
$(this).clone().insertBefore(this);
});
You don't need anything fancy here. If you attach the onchange event with jQuery/javascript instead of in the HTML, then clone( ) will only create one copy, as desired.
HTML:
<input type="file" id="fileUpLoad" />
JS:
$(document).ready( function() {
$('#fileUpLoad').change( function() {
doFunction();
});
});
doFunction = function() {
var oldEl = $('#fileUpLoad');
var newEl = $(oldEl).clone( );
$(oldEl).attr('id','fileUpLoadOLD');
$(oldEl).before(newEl);
}
P.S fileUpLoad, really? That sneaky capital L in there caused me some problems.
Building on the answer from #undefined (which works) I'd suggest actually making the change event a delegated event on a container object. Otherwise you end up with one input (the old one) which clones itself on change and another (the new one) which doesn't.
HTML:
<div class="container">
<input type="file" class="foo" id="fileUpLoad" />
</div>
Javascript:
$(function () {
$('.container').on('change', '.foo', function () {
var oldEl = $('#fileUpLoad');
var newEl = $(oldEl).clone().val('');
$(oldEl).attr('id', 'fileUpLoadOLD');
$(oldEl).before(newEl);
});
});
Working fiddle (I have also reset the value of the new input to null)
Note: This is still crashing Firefox for me. Not sutre why but might be my FF - the change event hasn't fired (in the other browsers) at the point FF crashes.
Explanation
I think what is happening here is that jQuery does not (by default) clone events when you clone an element. It does, however, clone attributes. So the onchange attribute on the element gets cloned and creates a change event on the cloned element, which for some reason is then called triggering an infinte loop. Not sure why .clone() triggers change events
Events attached via .on() on the object are not cloned - so no extra elements but no event on them either. By making the event a delegated event on a container we avoid the problem (since when the change event is triggered during clone the new element is not part of the page yet and so does not trigger the delegated event) but still have the functionality on the new object (once we insert it into the page).
You need better handling of the IDs if you want to be able to get more than two such inputs.

Using .on() and targeting elements with a specific attribute

I understand you can use .on() to attach a single click event to an element and then specify which child elements receive the click. So, for example:
$(this.el).on("click", "span", function () {
alert("Bloop!");
});
I need to be a bit more specific and target selectors with a particular attribute, like this:
$(this.el).on("click", "span[data-placeholder]", function () {
alert("Bloop!");
});
That doesn't seem to work, though. As soon as I add the attribute it stops working. No errors, just doesn't seem to find the elements.
Is that the expected behavior? Is there a way around it?
CLARITY
$(this.el) is just a div that contains a number of elements, some of which are <span data-placeholder="First Name"></span> tags. There could be dozens of those <span> tags and I didn't want that many event listeners, so I thought I'd use .on() to add the click to the parent container.
Here's JSFiddle showing your example working, with both existing <span>s and with newly created ones.
Just to be clear, this will work with your event delegation:
var span = $('<span>Test</span>');
span.attr('data-placeholder', 'test'); // declare as an attribute
$(this.el).append(span);
span.click();
This will not:
var span = $('<span>Test</span>');
span.data('placeholder', 'test'); // declare with .data()
$(this.el).append(span);
span.click();
jQuery's .data() method will read properties from data attributes if declared, but does not store them as attributes on the element when adding data.
Here's another JSFiddle.
try
$("span[data-placeholder]", this.el).on("click", function () {
alert("Bloop!");
});
You can choose to filter your spans
$('span', this.el).filter(function() {
return $(this).hasAttr('data-placeholder');
}).on('click', function() {
//This is for all the spans having data-placeholder
//...
});
Or if the placeholder is set via data api:
$(this.el).filter(function() {
return $(this).data('placeholder') != 'undefined';
}).on('click', function() {
//This is for all the spans having data-placeholder
//...
});
This functions above select those elements specifically, if event delegation on the OP is needed, then you can do the following:
$('span', this.el).on('click', 'span', function() {
if($(this).data('placeholder') != 'undefined') {
alert('bloop');
}
});
add a id to your span and pin point it using # tag

Categories

Resources