Javascript: script for menu not reacting when menu is dynamically created - javascript

I have this menu, statically, it is as follows:
<div class="dropdown col-sm-3">
<button type="button"
class="btn btn-select btn-block"
data-toggle="dropdown">Plane <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-select" id="plane">
<li><label class="dropdown-radio"> <input type="radio" value="3" name="plane"> Plane: A360 </label></li>
<li><label class="dropdown-radio"> <input type="radio" value="2" name="plane"> Plane: AR45 </label></li>
<li><label class="dropdown-radio"> <input type="radio" value="1" name="plane"> Plane: A380 </label></li>
</ul>
</div>
...
<script>
$('.dropdown-radio').find('input').change(function() {
var dropdown = $(this).closest('.dropdown');
var radioname = $(this).attr('name');
var checked = 'input[name=' + radioname + ']:checked';
//update the text
var checkedtext = $(checked).closest('.dropdown-radio').text();
dropdown.find('button').text( checkedtext );
//retrieve the checked value, if needed in page
var thisvalue = dropdown.find( checked ).val();
console.log( thisvalue );
});
</script>
This version worked: the user could select one (and only one) option, and the option would then be displayed in the main button. I have now created this same menu dynamically with data from a MySQL database.
<script>
var socket = io.connect('http://localhost:3000');
socket.on('sess', function(message) {
if(message == 0){
alert('No planes associated.');
}else{
for(var i = 0; i < message.length; i++){
$('#session').prepend( '<li><label class="dropdown-radio" > <input type="radio" value="'
+ message[i].idSession + '" name="session"> Session: '
+ message[i].idSession + ' </label></li>');
}
}
});
</script>
Here the menu still drops down but now the user can check as many radios as they want and the first script isn't activated (nothing appears in the console). I have tried putting the second script both before and after the previous one, both give the same results.
Is there a workaround for this? I'm new to Javascript (jQuery) and its sensibilities.
Thanks

What you want is to bind the change event to a higher on hierarchy element rather then directly at the inputs that didn't exist yet:
$('#plane').on('change', 'input', function() {
var dropdown = $(this).closest('.dropdown');
var radioname = $(this).attr('name');
var checked = 'input[name=' + radioname + ']:checked';
//update the text
var checkedtext = $(checked).closest('.dropdown-radio').text();
dropdown.find('button').text( checkedtext );
//retrieve the checked value, if needed in page
var thisvalue = dropdown.find( checked ).val();
console.log( thisvalue );
});
Doing so, you are binding the change event to the parent div, so doesn't matter how you modify its children on a later moment, it will always fire an event for it if the target element was an input (second function argument).
You don't need to execute this script AFTER the inputs were loaded, just go with your initial approach on dynamically loading content but tweak that change with the on('change' version.

I've combined my comments to an answer for easier reading/for anyone else looking at this.
The first script runs once inline of the page load and due to the asynchronous call to your database, that means it doesn't find anything to bind change to and then the script is never run again. You'll need to update your script so that it is run again after the new radio buttons have all been loaded into the DOM (this is after in time, not on the page).
Consider saving your first script to a function and calling it from your database call success, post rendering your radio buttons. You may need to use .off('change') also to remove any existing change bindings before creating new ones.
With regards to your radio buttons allowing multiple selected, as long as they share a name then they shouldn't allow multiple as per the documentation.

Related

Setting values of dynamically added input fields changing all input fields

I have a form where users can create recipes. I start them off with one ingredient field (among others) and then use .append() to add as many more as they want to the div container that holds the first ingredient. The first input field has an id of IngredientName1 and dynamically added input fields are IngredientName2, IngredientName3, etc.
When they start typing in the input field, I pop a list of available ingredients filtered by the value they key into IngredientNameX. When they click on an ingredient in the list, it sets the value of the IngredientNameX field to the text from the div - like a search & click to complete thing. This all works very well; however, when you add IngredientName2 (or any beyond the one I started them with initially) clicking on an available ingredient sets the values of every single IngredientNameX field. No matter how many there are.
I hope this is enough context without being overly verbose, here's my code (I've removed a lot that is not relevant for the purpose of posting, hoping I didn't remove too much):
<div id="ingredientsContainer">
<input type="hidden" id="ingredientCounter" value="1">
<div class="ingredientsRowContainer">
<div class="ingredientsInputContainer"><input class="effect-1 ingredientsInput" type="text" name="IngredientName1" placeholder="Ingredient" id="IngredientName1" data-ingID="1"><span class="focus-border"></span></div>
</div>
<input type="hidden" name="Ingredient1ID" id="Ingredient1ID">
</div>
<script>
$(document).ready(function(){
$(document).on('keyup', "[id^=IngredientName]",function () {
var value = $(this).val().toLowerCase();
var searchValue = $(this).val();
var valueLength = value.length;
if(valueLength>1){
var theIngredient = $(this).attr("data-ingID");
$("#Ingredients").removeClass("hidden")
var $results = $('#Ingredients').children().filter(function() {
return $(this).text() === searchValue;
});
//user selected an ingredient from the list
$(".ingredientsValues").click(function(){
console.log("theIngredient: "+theIngredient);//LOGS THE CORRECT NUMBER
var selectedIngredientID = $(this).attr("id");
var selectedIngredientText = $(this).text();
$("#IngredientName"+String(theIngredient)).val(selectedIngredientText);//THIS IS WHAT SETS EVERYTHING WITH AN ID OF IngredientNameX
$("#Ingredient"+String(theIngredient)+"ID").val(selectedIngredientID);
$("#Ingredients").addClass("hidden");
});
$("#Ingredients *").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
} else {
$("#Ingredients").addClass("hidden")
}
});
$("#AddIngredient").click(function(){
var ingredientCounter = $("#ingredientCounter").val();
ingredientCounter++;
$("#ingredientCounter").val(ingredientCounter);
$('#ingredientsContainer').append('\
<div class="ingredientsRowContainer">\
<div class="ingredientsInputContainer"><input class="effect-1 ingredientsInput" type="text" name="IngredientName'+ingredientCounter+'" placeholder="Ingredient" id="IngredientName'+ingredientCounter+'" data-ingID="'+ingredientCounter+'"><span class="focus-border"></span></div>\
</div>\
<input type="hidden" name="Ingredient'+ingredientCounter+'ID" id="Ingredient'+ingredientCounter+'ID">\
');
});
});
</script>
[UPDATE] I realized the problem is happening because the function is running multiple times. I assume this happening because I'm calling a function on keyup of a field whose id starts with IngredientName so when one has a key up event, all existing fields run the function. How do i modify my:
$(document).on('keyup', "[id^=IngredientName]",function () {
to only run on the field with focus?

Manipulating Text with JavaScript Based on Checkbox State

I'm trying to create a simple HTML page that presents a user with several options via checkboxes. I need to generate a string, stored in a variable that I can use on the page when a button is clicked, which will vary based on which boxes are checked.
The string will be a URL ("http://example.com/index.htm&term=") and will need to have additional text appended to it for each checkbox that is checked.
For example, if only a single box, say box1, is checked the string "box1" should be appended to the URL variable to look like "http://example.com/index.htm&term=box1"
If, however more than one box is checked, say box2 and box3 are checked, then the string "box2%20OR%20box3" should be appended to the URL string.
I'm pretty sure this can be done with JavaScript but I have no experience with it and would appreciate some guidance/examples.
Instead of storing it in a variable, I would recommend calling a function that builds the link when the button is pressed. If you really wanted to put it in a variable though, you would set up an event listener for the change event for each checkbox, and call the function to update the variable each time one of the checkboxes is checked or unchecked.
function checkboxUrl(checkboxes) {
const
url = `http://example.com/index.html`,
checkedArray = [];
for (let checkbox of checkboxes) {
if (checkbox.checked) checkedArray.push(checkbox);
};
const checkboxString = checkedArray.map(checkbox => checkbox.value).join(`%20OR%20`);
return url + (checkboxString ? `?term=` + checkboxString : ``);
}
let checkboxes = document.querySelectorAll(`input[type='checkbox']`);
label {
display: block;
}
<label><input type='checkbox' value='box1'>box1</label>
<label><input type='checkbox' value='box2'>box2</label>
<label><input type='checkbox' value='box3'>box3</label>
<button onclick='console.log(checkboxUrl(checkboxes))'>Get URL</button>
If you use Jquery you can do something like this:
<input type="checkbox" id="box1">
<input type="checkbox" id="box2">
<button type="button" id="myButton">Submit</button>
<script>
$(document).ready(function(){
$('#myButton').click(function(){
var url = 'www.myurl.com/index.html&term=';
var checkboxList = [];
var params = '';
$(':checkbox:checked').each(function(){
checkboxList.push($(this).attr('id'));
});
params = checkboxList.join('%'); //will output "box1%box2"
url += params //www.myurl.com/index.html&term=box1%box2
window.location.href = url;
});
});
</script>

Dynamic created HTML does not reload

I'm having a kind of problem that I think is related to how I generate my HTML... I use a JavaScript function to generate some HTML, but then it begins to misfunction... let me first paste some of my code
First, my raw HTML
<div id="effect">
<label for="s_effect">Effect: </label>
<select name="s_effect" id="s_effect" onchange="ne();">
<option value="">Select your Effect</option>
</select>
<div id="effect_description"></div>
<div id="effect_options"></div>
</div>
Then, I have a function that loads "s_effect" based on an array (that's fine and working, np).
Then, my ne() (new effect) function:
function ne(){
reset();
e = g('s_effect');
if(newEffect(e.options[e.selectedIndex].value)){
console.log("new effect created");
updateScreen();
}
}
It basically "resets" parts of the screen (error tracking and that, stuff not related with my problem), then calls to updateScreen() (note: g function is just a synonym for document.getElementById)
It goes to this function (sorry it's a lot of code...)
function updateScreen(){
if(project.effect instanceof Effect){
lock("instant");
lock("event");
showDescription();
generateOptions();
}else if(project.effect == null){
unlock("instant");
unlock("event");
}
if(project.check()){
generateButton();
}else{
npButton();
}
}
That basically, locks some part of the window, then get some HTML on calls below.
generateDescription(), the part is giving trouble, does the following:
function generateOptions(){
g('effect_options').innerHTML = '';
effectID = project.effect.calculateId();
if(effectID === false)
return false;
g('effect_options').innerHTML = project.effect.parameters.HTMLOptions;
return true;
}
It calls to an object attribute that basically dumps some HTML code:
<div>
<label for="1_color">Color: </label><input type="color" id="1_color" name="1_color" onchange="updateColor('1_color');" value="#FFFFFF">
<input type="text" name="1_color_rgb" id="1_color_rgb" onchange="updateColor('1_color_rgb');" value="#FFFFFF">
</div>
<div id="extra"></div>
<div>
<input type="button" value="Add Node" onclick="addNode();">
</div>
Finally, addNode() makes an innerHTML += [new div on "extra"] but increasing the number (2_color, 2_color_rgb, and so on).
function addNode(){
var count = ++(project.effect.parameters.colorCount);
g('extra').innerHTML +=
'<div><label for="' + count + '_color">Color: </label><input type="color" id="' + count + '_color" name="' + count + '_color" onchange="updateColor(\'' + count + '_color\');" value="#FFFFFF" />' +
'<input type="text" name="' + count + '_color_rgb" id="' + count + '_color_rgb" onchange="updateColor(\'' + count + '_color_rgb\');" value="#FFFFFF" /></div>' +
}
To this point everything is working fine, even "updateColor" works (it updates the value of each input so you can choose a color by filling any input).
The problem comes when I modify any x_color or x_color that has been added via button... It adds a new "node" but restarts the values of previous inputs.
I debugged it, and by the point is doing the adding, the innerHTML of "extra" shows all inputs with "#FFFFFF" values (initial), but on the screen, the values are right...
Any help with this may be appreciated.
PS: I'm using chrome.
Thank you!
Just to clarify, as #Forty3 answered, the problem was the fact that I was modifying the innerHTML each time, making my browser to re-render extra each time.
As he suggested, I edited my function, now looks like
function addNode(){
var count = ++(project.effect.parameters.colorCount);
var nDiv = document.createElement("div");
nDiv.innerHTML = "whatever I was putting...";
g('extra').appendChild(nDiv);
}
Now it works fine.
Thank you all for the support.
The issue, it appears, is that by reassinging the .innerHTML property of the g('extra') DOM element, you are telling the browser to re-render the element based on the HTML -- not the DOM elements and values contained within.
In other words, when you add a color, the g('extra').innerHTML gets updated with the new HTML to render an additional color selection block (i.e. 2_color). When a user then picks a new color, the browswer will update the value for the 2_color DOM element but doesn't necessarily update the innerHTML property for g('extra'). Then, when another color block is added, the innerHTML is updated once more and the browser re-renders it thereby "resetting" the values.
Instead of constructing the additional controls using HTML string, use DOM manipulation (e.g. .createElement() and .append()) in order to add your new options.

Jquery multiple plus/minus script

I have multiple forms on a page and also multiple input boxes with plus/minus signs.
I'm having trouble to get those input boxes to work seperately. Probably because of some wrong/same id's or something like that or maybe a wrong setup of my code. The thing is I can't find my error in the code and I don't get any errors in my console.
What I have:
function quantity_change(way, id){
quantity = $('#product_amount_'+id).val();
if(way=='up'){
quantity++;
} else {
quantity--;
}
if(quantity<1){
quantity = 1;
}
if(quantity>10){
quantity = 10;
}
$('#product_amount_'+id).val(quantity);
}
And my html:
//row 1
<div class="amount"><input type="text" name="quantity" value="1" id="product_amount_1234"/></div>
<div class="change" data-id="1234">
+
-
</div>
//row 2
<div class="amount"><input type="text" name="quantity" value="1" id="product_amount_4321"/></div>
<div class="change" data-id="4321">
+
-
</div>
I thought something like this would do the trick but it doesn't :(
$(document).ready(function(){
$('.change a').click(function(){
var id = $(this).find('.change').data('id');
quantity_change(id)
});
});
Any help greatly appreciated!
You should use closest() method to get access to the parent div with class change, then you can read the data attribute id's value.
var id = $(this).closest('.change').data('id');
alert(id);
Since you are already binding the click event using unobutrusive javascript, you do not need the onclick code in your HTML markup.
Also your quantity_change method takes 2 parameters and using both, but you are passing only one. You may keep the value of way in HTML 5 data attributes on the anchor tag and read from that and pass that to your method.
<div class="change" data-id="1234">
+
-
</div>
So the corrected js code is
$(document).ready(function(){
$('.change a').click(function(e){
e.preventDefault();
var _this=$(this);
var id = _this.closest('.change').data('id');
var way= _this.data("way");
quantity_change(way,id)
});
});
Here is a working sample.

add templated div to page multiple times with different ids

I am using ASP.Net MVC along with Jquery to create a page which contains a contact details section which will allow the user to enter different contact details:
<div id='ContactDetails'>
<div class='ContactDetailsEntry'>
<select id="venue_ContactLink_ContactDatas[0]_Type" name="venue.ContactLink.ContactDatas[0].Type">
<option>Email</option>
<option>Phone</option>
<option>Fax</option>
</select>
<input id="venue_ContactLink_ContactDatas[0]_Data" name="venue.ContactLink.ContactDatas[0].Data" type="text" value="" />
</div>
</div>
<p>
<input type="submit" name="SubmitButton" value="AddContact" id='addContact' />
</p>
Pressing the button is supposed to add a templated version of the ContactDetailsEntry classed div to the page. However I also need to ensure that the index of each id is incremented.
I have managed to do this with the following function which is triggered on the click of the button:
function addContactDetails() {
var len = $('#ContactDetails').length;
var content = "<div class='ContactDetailsEntry'>";
content += "<select id='venue_ContactLink_ContactDatas[" + len + "]_Type' name='venue.ContactLink.ContactDatas[" + len + "].Type'><option>Email</option>";
content += "<option>Phone</option>";
content += "<option>Fax</option>";
content += "</select>";
content += "<input id='venue_ContactLink_ContactDatas[" + len + "]_Data' name='venue.ContactLink.ContactDatas[" + len + "].Data' type='text' value='' />";
content += "</div>";
$('#ContactDetails').append(content);
}
This works fine, however if I change the html, I need to change it in two places.
I have considered using clone() to do this but have three problems:
EDIT: I have found answers to questions as shown below:
(is a general problem which I cannot find an answer to) how do I create a selector for the ids which include angled brackets, since jquery uses these for a attribute selector.
EDIT: Answer use \ to escape the brackets i.e. $('#id\\[0\\]')
how do I change the ids within the tree.
EDIT: I have created a function as follows:
function updateAttributes(clone, count) {
var f = clone.find('*').andSelf();
f.each(function (i) {
var s = $(this).attr("id");
if (s != null && s != "") {
s = s.replace(/([^\[]+)\[0\]/, "$1[" + count + "]");
$(this).attr("id", s);
}
});
This appears to work when called with the cloned set and the count of existing versions of that set. It is not ideal as I need to perform the same for name and for attributes. I shall continue to work on this and add an answer when I have one. I'd appreciate any further comments on how I might improve this to be generic for all tags and attributes which asp.net MVC might create.
how do I clone from a template i.e. not from an active fieldset which has data already entered, or return fields to their default values on the cloned set.
You could just name the input field the same for all entries, make the select an input combo and give that a consistent name, so revising your code:
<div id='ContactDetails'>
<div class='ContactDetailsEntry'>
<select id="venue_ContactLink_ContactDatas_Type" name="venue_ContactLink_ContactDatas_Type"><option>Email</option>
<option>Phone</option>
<option>Fax</option>
</select>
<input id="venue_ContactLink_ContactDatas_Data" name="venue_ContactLink_ContactDatas_Data" type="text" value="" />
</div>
</div>
<p>
<input type="submit" name="SubmitButton" value="AddContact" id='addContact'/>
</p>
I'd probably use the Javascript to create the first entry on page ready and then there's only 1 place to revise the HTML.
When you submit, you get two arrays name "venue_ContactLink_ContactDatas_Type" and "venue_ContactLink_ContactDatas_Data" with matching indicies for the contact pairs, i.e.
venue_ContactLink_ContactDatas_Type[0], venue_ContactLink_ContactDatas_Data[0]
venue_ContactLink_ContactDatas_Type[1], venue_ContactLink_ContactDatas_Data[1]
...
venue_ContactLink_ContactDatas_Type[*n*], venue_ContactLink_ContactDatas_Data[*n*]
Hope that's clear.
So, I have a solution which works in my case, but would need some adjustment if other element types are included, or if other attributes are set by with an index included.
I'll answer my questions in turn:
To select an element which includes square brackets in it's attributes escape the square brackets using double back slashes as follows: var clone = $("#contactFields\[0\]").clone();
& 3. Changing the ids in the tree I have implemented with the following function, where clone is the variable clone (in 1) and count is the count of cloned statements.
function updateAttributes(clone, count) {
var attribute = ['id', 'for', 'name'];
var f = clone.find('*').andSelf();
f.each(function(i){
var tag = $(this);
$.each(attribute, function(i, val){
var s = tag.attr(val);
if (s!=null&& s!="")
{
s = s.replace(/([^\[]+)\[0\]/, "$1["+count+"]");
tag.attr(val, s);
}
});
if ($(this)[0].nodeName == 'SELECT')
{ $(this).val(0);}
else
{
$(this).val("");
}
});
}
This may not be the most efficient way or the best, but it does work in my cases I have used it in. The attributes array could be extended if required, and further elements would need to be included in the defaulting action at the end, e.g. for checkboxes.

Categories