JQuery .on only firing once on dynamically generated elements - javascript

So I have the following problem. I am trying to add a two events to a table of checkboxes.
Here's an example of the html.
<body>
<div id='container'> //static element, everything beyond this element is dynamic
<div id='pane_x'>
<div id='bottom_x'>
<div id='bottom_left_x'>
<div id='results_x'>
<div id='list_x'>
<div id='table_1'>
<table>
<tbody>
<tr>
<td>
<input type='checkbox' name='blah' id='blah_obj.id_x' class='blahblah'>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
I am trying to select the checkboxes only hopefully by using a prefix selector [id^='blah_']. I am able to get the code working for the first pane ie: pane_0, but it doesn't fire on pane_1 or beyond.
jquery(document).on("change", "[id^='blah_" + obj.id + "']", function() {
//code here
});
There may be nesting errors as I just made an approximate estimate of the html. The weird thing is I can see them by using a daisy chained .children() statement, but I can't select the input elements.
Since the comments don't support the same code blocks as this section I'll just add it here:
jquery(document).on("change", ".checked", function() {
var ids = jquery(this).attr("id").split("_");
if (ids[0] === "blah")
//do code
}
});
edited the id for clarity. The id structure is "blah_" obj.id "_" individual iterator. So 3 checkboxes on pane 0 would look like this:
blah_0_0
blah_0_1
blah_0_2
there are 2 other sets of checkbox lists on this page that I do not want to target with these functions, that is why i'm using the prefix selector.

The point of using startsWith selector is not to try to complete the whole value as you are doing with obj.id if obj.id is even defined.
Following would find all input with ID's starting with 'blah_' either existing or in the future.
jQuery(document).on("change", "input[id^='blah_']", function() {
//code here
});
Using the class as selector makes even more sense if they all share a common class
jQuery(document).on("change", "input.blahblah", function() {
//code here
});
Also note you have typo in jquery should be jQuery unless you have defined it otherwise in your own code
Important note ID's may not be repeated in a page, in case you have been repeating the same checkbox ID. They must be unique by definition

here another sample :
$("body").on({
click:function(){
// where $(this) is the current item changed
}
}, "input.blahblah");
​

Related

Replacing a number found on multiple instances in div element with another number

As you can see, my HTML contains multiple references to '0'. I need to change these to '1'.
However, my jQuery isn't working.
jQuery(".create-new-location").click(function() {
jQuery("#header-logo").html().replace(/\[0\]/g, '['+(1)+']');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="header-logo" class="header-title location-header-0 title-edit-header" data-row-id="location-header-0" title="Location name (for your reference)">
<div class="input-selection title-edit-header">
<div class="text-input">
<input class="option_textbox col-sm-12 change-width title-edit" placeholder="Location name (for your reference)" value="" type="text" name="bp_theme_options[social][map][locations][0][location_name]">
</div>
</div>
<div class="open-block pencil-edit" data-row-id="location-header-0"></div>
</div>
You have to set the html like this
jQuery(".create-new-location").click(function() {
var the_html = jQuery("#header-logo").html().replace(/\[0\]/g, '['+(1)+']');
jQuery("#header-logo").html(the_html);
});
But this is not a good practice!!
When you need to change only the attribute of an <input>, why change the whole #header-logo, right? When you re-draw html like this, you risk losing event-handlers binded to the elements you have just re-drawn.
jQuery(".create-new-location").click(function() {
var elements = jQuery("#header-logo").find('input[name]'); /*all input with name*/
elements.each(function(el){
var the_name = el.attr('name').replace(/\[0\]/g, '['+(1)+']');
el.attr('name', the_name);
});
});
Regexing the html is never a good idea.
As you can see, my HTML contains multiple references to '0'. I need to change these to '1'.
The approach you used, and even the accepted answer here, will not modify the containing div with id="header-logo" which contains several of these references. Moreover, there are significant issues with simply replacing existing dom elements with freshly regexed ones in validation cases (as in, this may break your validation).
The approach you should use is to specifically target the attributes that contain these references, and then only modify those. Here is a general approach which looks in all attributes and modifies the occurrence of [0 (0 being the value of before) into [1 (1 being the value of after) as well as modifying the occurrence of -0 (before = 0) to -1 (after =1).
This will prevent removing any existing event handlers from the elements, as well as a number of other issues associated with regexing straight html and then replacing the dom element with the that result.
$.fn.indexUpdate = function(before,after){
$("*",this).add(this).each(function(){
$(this.attributes).each(function(){
this.value = this.value.replace(new RegExp('\\b\\-'+before+'\\b','g'), '-'+after);
this.value = this.value.replace(new RegExp('\\['+before, 'g'), '['+after);
});
});
};
$("#header-logo").indexUpdate(0,1);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="header-logo" class="header-title location-header-0 title-edit-header" data-row-id="location-header-0" title="Location name (for your reference)">
<div class="input-selection title-edit-header">
<div class="text-input">
<input class="option_textbox col-sm-12 change-width title-edit" placeholder="Location name (for your reference)" value="" type="text" name="bp_theme_options[social][map][locations][0][location_name]">
</div>
</div>
<div class="open-block pencil-edit" data-row-id="location-header-0"></div>
</div>
This statement jQuery("#header-logo").html().replace(/\[0\]/g, '['+(1)+']'); retrieve the html inside the element that have id as header-logo and replace every 0 inside the html string with 1 But it doesn't assign the modified string again to the element So you may want to use following code.
jQuery("#header-logo").html(jQuery("#header-logo").html().replace(/\[0\]/g, '['+(1)+']'));
Try this:It will replace all existence of '0' with '##'
$(".create-new-location").click(function() {
$("#header-logo").html().replace(/0/gi, '##')
});

How do disabled button in Script onClick even in mvc 4 razor

Hi I got a issue with JavaScript when user click on add button in table the add button will disabled which is working but not properly. it was giving issue with some Id like user click on Id="rbutton_1" then it will disable all button with Id Id="rbutton_10", Id="rbutton_11" which i want to fix My JavaScript.
Here is a HTML Table view
#foreach (var item in this.Model.CartList)
{
<tr>
<td class="i"><img class="i" src="#item.IconUrl"></td>
<td>#item.ServiceName</td>
<td><span class="fa fa-inr"></span><span>#Convert.ToInt32(#item.TotalPrice)</span></td>
<td>
<span class="glyphicon glyphicon-trash"></span>
</td>
</tr>
}
Here is a Script on click event below:
function onClick(e, Id) {
e.preventDefault();
var url = '#Url.Action("CartAdd", "Home")/' + Id;
$.get(url, function (data) {
$(".ref").load("Detail .ref");
$(".t").find("a[id*='rbutton_" + Id + "']").attr("disabled", "disabled").attr("onClick", "this.disabled=false");
});
}
function onRemove(e, Id) {
var url = '#Url.Action("CartRemove", "Home")/' + Id;
$.get(url, function (data) {
$(".ref").load("Detail .ref");
$(".t").find("a[id*='rbutton_" + Id + "']").attr("disabled", false).attr("onClick", "this.disabled=true");
});
}
This is code is helping to disable button but giving issue above I have written
$(".t").find("a[id*='rbutton_" + Id + "']").attr("disabled", "disabled").attr("onClick", "this.disabled=false");
You have following ways of doing this type of structure.
If your rows are fixed you can either split your rows in two parts and render them in separate table.
OR
You can render 6 TD tags instead of 2 and render two records on one row.
OR
It would personally suggest not to use TABLE and go for DIV structure where you will have more flexibility and structure will easily adjust as per rows of your dataset like following.
<div>
#foreach (var item in this.Model.ServiceList)
{
<div style="float:left;">
<div>
<img class="i" src="#item.IconUrl" />
</div>
<div>
<span>#item.Services</span>
</div>
<div>
+ add
</div>
<div><span class="fa fa-inr"></span><span>#Mobilappy.Models.Detail.Price(#item.Id)</span>
</div>
</div>
}
</div>
You may need to add some more styles to align it properly. If you are open to use any third party CSS libraries, i would suggest to use Bootstrap and check "col-md-*" class which can do this task easily for you.
You can also use beuty of CSS Tables with "display:table" , "display:table-cell","display-table-row" if you don't want to use third party CSS lib.
EDIT
The above answer is for the question you asked to fix Structure of your page.
To fox issue which you just updated on your question.. you have to try following script
$(".t").find("a[id='rbutton_" + Id + "']").attr("disabled", "disabled").attr("onClick", "this.disabled=false");
Remove "*" as it will select all elements otherwise.
You can use Take and Skip. For the left side table use:
var tableRowsLeft = this.Model.ServiceList.Take(10);
And for the right table
var tableRowsRight = this.Model.ServiceList.Skip(10).Take(10);
But it depends on how many rows you might need to handle(this example is not very suitable if you have thousands of rows), and it would be best to handle that kind of logic in the Controller instead of in The View
Just remove * from the Selection of element as below * use as WILDCARDS in jquery so it will disabled all anchor link which will match with this string rbutton_1 so these anchor tags with this like rbutton_10 and rbutton_11will be disabled.
See also this jquery documentation for more information
$(".t").find("a[id='rbutton_" + Id + "']").attr("disabled", "disabled").attr("onClick", "this.disabled=false");

Add listener to dynamically added HTML within closure?

I have a list of locations in google maps that a user can click. When they click, I generate a template that displays the information for that place. Right now, I have a container div that I call .replaceWith() on with the newly generated template.
When a user tries to click a button in that template, I want a place-specific action to take place (e.g. pin the place). To do this, I need the place ID saved somewhere so that the listener code knows what to do. I was hoping to use a closure to create the listener on the fly so that I could "enclose" the ID of the place that the viewer is getting details for:
function selectPlace(place_id) {
// Swap out the old template
$("#listing-placeholder").replaceWith(listing_info_template(place));
// Create a listener to handle clicks while remembering the place_id
(function(){
$('.save_button').click(function(){
alert("Clicked handler for " + place_id);
});
})();
}
This doesn't seem to work at all; my listener never fires. It seems like this is related to the fact that .save_button is inside of the template (dynamically added). Not sure why this is.
I am hesitant to use .on() because then I would have to put the ID somewhere in the template, which feels really hacky to me. I know that backbonejs somehow binds events to dynamically inserted templates so that any relevant context is still available to events -- that's effectively what I'm looking to mimic here.
So, any suggestions on how I can add a listener to a dynamically created element in such a way that the listener receives key information about the "object" that element is conceptually representing, without bloating the HTML with extra metadata (I've seen people make the id's like "save_button-" and then split on the dash, but that seems extremely hacky?)?
Thanks!
I've used a variable with a broad scope to hold selected Id in the quick and dirty example below.
var selectedId; //Holds seleced ID
function selectPlace(place_id) {
// Swap out the old template
//$("#listing-placeholder").replaceWith(listing_info_template(place));
$("#listing-placeholder").replaceWith($("#section" + place_id));
//Sotre the ID here
selectedId = place_id;
}
$(document).ready(function() {
$("#cbId").change(function() {
selectPlace($(this).val())
});
//Set up event listener. Change #target as appropriate for you
$("#target").on("click", ".save_button", function() {
alert(selectedId);
});
});
#sections {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div>
<label>Select a section
<select id="cbId">
<option></option>
<option>1</option>
<option>2</option>
<option>3</option>
</select>
</label>
</div>
<div id="target">
<div id="listing-placeholder">
I'm the place holder
</div>
</div>
<div id="sections">
<!--No Event Listeners Will be bound directly untill template is replaced -->
<div id="section1">
<h2>I'm section 1</h2>
<input type="button" value="Save Button" class="save_button">
</div>
<div id="section2">
<h2>I'm section 2</h2>
<input type="button" value="Save Button" class="save_button">
</div>
<div id="section3">
<h2>I'm section 3</h2>
<input type="button" value="Save Button" class="save_button">
</div>
</div>
I will just try to correct your actual approach by passing the parameter to your closure, but you may find a better approach.
In your case you need to pass the place_id as a parameter to the closure too, this is how should be your code:
function selectPlace(place_id) {
// Swap out the old template
$("#listing-placeholder").replaceWith(listing_info_template(place));
// Create a listener to handle clicks while remembering the place_id
(function(id){
$('.save_button').click(function(){
alert("Clicked handler for " + id);
});
})(place_id);
}
I changed the variable name so you can get the difference between the two parameters.
You actually have to use .on, it's the way jQuery matches all dynamically added elements. But you don't need to do anything "hacky", you just have to bind the listener to a parent element
`$(parent).on('click', '.save_button', {});
function selectPlace(place_id) {
// Swap out the old template
$("#listing-placeholder").replaceWith(listing_info_template(place));
// Create a listener to handle clicks while remembering the place_id
$(document).on('click', '.save_button').click(function(){
alert("Clicked handler for " + place_id);
});
}

Checking the names of elements from an array of objects selected by jquery

I'm a beginner in js and jquery library. I'd like to get an array of input fields with a particular name, and validate input. Each of my input fields have a name like NS[0], NS[1] etc. The total number of fields will have to be determined by the code, since the fields are generated by javascript.
I know that I can have jquery address the individual object like this:
$("input[name=NS\\[0\\]]").val() for <input type="text" name="NS[0]">.
However, how can I get an array of all these similiar elements, from NS[0] to NS[x] where x has to be determined based on how many fields have been generated? I already have other fields with different name patterns sharing the same css class, so using class is not an option. These boxes are in a particular div area, but in the same area are other input fields, so choosing all input boxes of the same area selects them as well.
In other words, how do I use jquery to check the name of each input field, after getting the entire array of input fields, to check each individual name?
Since I have input fields of various names in the area determined by the table id CNTR1, I would select them with $('#CNTR1 input'). I can also select individual fields by using $("input[name=]"). However, what I want to do, is to select everything under $('#CNTR1 input'), and then run a loop on their names, checking whether the names match a predetermined criteria. How can I do that?
The html code:
<table class="table" id="cnservers">
<thead>
<tr>
<th>Type</th>
<th>Preference</th>
<th>Value</th>
<th>Name</th>
</tr>
</thead>
<tr id="CNTR0">
<td>CNAME</td><td><input type="text" name="CN_PREF[0]" value=""></td><td>
<input type="text" name="CN_VAL[0]" value=""></td><td>
<input type="text" name="CN_NAME[0]" value="">
<a class="btn btn-danger" onclick="DelField(this.id);" id="CN_D0" >
<span class="btn-label">Delete
</span>
</a>
<a class="btn btn-primary" onclick="addField('cnservers','CN',10);" id="CN_A0" >
<span class="btn-label">Add
</span>
</td></tr>
</table>
[1]: http://i.stack.imgur.com/bm0Jq.jpg
I must be missing something. Is there a reason you can't use the http://api.jquery.com/attribute-starts-with-selector/?
$('#CNTR1').find('input[name^="NS"]')
Regarding,
However, what I want to do, is to select everything under $('#CNTR1 input'), and then run a loop on their names, checking whether the names match a predetermined criteria. How can I do that?
$("#CNTR1 input").each(function(index, elem) {
var $elem = $(elem),
name = $elem.attr('name');
var nameMatchesCondition = true; // replace with your condition
if (nameMatchesCondition) {
// do something!
}
});
EDIT 1:
Well, id is still an attribute of an html element. So you could do $('[id^="CNTR1"]') ... The value of the id attribute of an element doesn't contain the #. It's only part of the css/jquery selector. When using attribute style selectors, you don't need it. Though I can't comment on the performance of this.
Ideally, you want to attach a second class, say js-cntr to all elements that you created with an id starting with CNTR. Even though different name pattern elements may already have one class, that class is for styling. There is no stopping you from attaching custom classes purely for selection via js. This is an accepted thing to do and which is why the class name starts with js-, to denote that its purely for use via js for selection.
Try this
HTML
<table id="CNTR1">
<tr>
<td>CNAME</td>
<td><input type="text" name="CN_PREF[1]" id="CN_IN[1]"></td>
<td><input type="text" name="CN_VAL[1]"></td>
<td><input type="text" name="CN_NAME[1]"></td>
</tr>
</table>
JS
$(document).ready(function(){
$("#CNTR1 input").each(function() {
console.log($(this).attr("name"));
// Match With predetermined criteria
});
});
Use jQuery's .filter method, with a filter function:
filterCritera = /^CN_NAME\[/; // or whatever your criteria is
var inputs = $('#CNTR0 input');
// you could also cache this filter in a variable
inputs.filter(function(index){
return filterCritera.test(this.name);
}).css('background','red');
jsbin
The markup you posted does not the markup described in your question ( it does not contain NS[0]) but you can substitute it in the reguluar expression above.

linking HTML checkbox with element to hide when checkbox is unchecked

What's a good pattern for adding additional data to an HTML element? For example, I'd like to link a checkbox to HTML I'd like to hide when the checkbox is unchecked. Like the for attribute of a label element, I want to specify the linkage in markup so I can write a simple, generic script to iterate through all checkboxes and hook up a jquery event handler to do the hiding/showing.
For example, in this HTML:
<input type="checkbox" id="showFoo" />
<div id="foo">
Some HTML here. Hide this when the checkbox is unchecked.
</div>
What's a good to let my script know that #showFoo is related to #foo? Ideally something that doesn't make my HTML non-validating or and doesn't require me to use a specific naming convention for IDs. Extra credit if it makes my script more efficient.
use a data-[key] attribute to identify what #showFoo should control
example jsfiddle
HTML:
<input type="checkbox" id="showFoo" data-toggles="foo" />
<div id="foo">
Some HTML here. Hide this when the checkbox is unchecked.
</div>
jQuery:
$('#showFoo').change(function() {
$('#' + $(this).data('toggles')).toggle();
});
This seems like a perfect case for data elements.
<input type="checkbox" id="showFoo" data-relateddiv="foo" />
Then in an event handler on the checkboxs:
$('#' + $(this).data("relateddiv")).show();
You can use the "rel" attribute
<input type="checkbox" id="showFoo" rel="foo" />
$('#showFoo').click(function(){
var element_id = $(this).attr('rel');
var element = $('#'+element_id);
if(element.is(':hidden')){
element.slideDown();
//element.show();
}
else{
element.slideUp();
//element.hide();
}
});
I use this currently
element.each(function(i, e) {
var checked = $(e).prop('checked'),
foo = */Relationship betweeen element and foo*/;
foo .toggleClass('invisibleClass', checked)
.toggleClass('visibleClass', !checked);
});
in case you have multiple foos and elements (you have to define the relationship between them first)
Run it on the event of your choice
Try below
if (checkboxIsChecked) {
foo.visibility:visible;
} else {
foo.visibility:hidden;
}

Categories

Resources