Issue Initializing select2 dynamically - javascript

Select2 initializes for the first item generated with on click and then on generating new div item with on click the select2 property is removed from the first item and initializes on the 2nd.
$(document).ready(function(){
$("#packages #add_package").on("click", function(){
$(this).before(
"<div class='col-12 packageCard'>"+
"<div class='col-4'>"+
"<div class='form-group'>"+
"<label>Container Type</label>"+
"<select name='cntType' id='cntType' class='form-control select2' data-dropdown-css-class='select2' style='width: 100%;'>"+
"<option value='0' selected='selected' disabled>Select Container Type</option>"+
"<option>20 feet</option>"+
"<option>40 feet</option>"+
"</select>"+
"</div>"+
"</div>"+
"</div>");
$('.select2').select2();
});
})

If you check your browser's devtools console, you will see a Javascript error thrown when you try to add the second select2:
Uncaught query function not defined for Select2 undefined
If you search for that error, you will find some other questions about this, for eg this one: "query function not defined for Select2 undefined error"
And if you read through the answers and comments, you will find several which describe what you are doing:
This comment:
This problem usually happens if the select control has already been initialized by the .select2({}) method. A better solution would be calling the destroy method first. Ex: $("#mySelectControl").select2("destroy").select2({});
This answer:
One of its other possible sources is that you're trying to call select2() method on already "select2ed" input.
Also this answer:
I also had this problem make sure that you don't initialize the select2 twice.
... and possibly more. These describe what you are doing - calling .select2() on elements which are already initialised as select2s.
The second time $('.select2').select2() runs, as well as trying to initialise your new select2, it is re-initialising the first select2 as a select2 again.
You have another problem in your code - every select has the same ID: id='cntType'. IDs must be unique on the page, so this is invalid HTML.
We can solve both problems at once by keeping track of how many selects you have on the page, and giving each new one an ID including its number, like say cntType-1, cntType-2, etc. Then we can target just the new ID to initialise it as a select2.
Here's a working example.
$(document).ready(function () {
// Track how many selects are on the page
let selectCount = 0;
$("#packages").on("click", function () {
// New select! Increment our counter
selectCount++;
//. Add new HTML, using dynamically generated ID on the select
$(this).before(
"<div class='col-12 packageCard'>" +
"<div class='col-4'>" +
"<div class='form-group'>" +
"<label>Container Type</label>" +
"<select name='cntType' id='cntType-" + selectCount + "' class='form-control select2' data-dropdown-css-class='select2' style='width: 100%;'>" +
"<option value='0' selected='selected' disabled>Select Container Type</option>" +
"<option>20 feet</option>" +
"<option>40 feet</option>" +
"</select>" +
"</div>" +
"</div>" +
"</div>");
// Initialise only our new select
$('#cntType-' + selectCount).select2();
});
})
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.0.0/select2.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/2.1.0/select2.min.js"></script>
<button id="packages">Add</button>

Related

Enable/Disable an HTML Element With Dynamically Generated Names/Tags

I have a table in HTML where the ID is dynamically generated from a row counter:
$(table).find('tbody').append("<tr>name=\"tableRow\"</tr>"
+ "<td>"
+ "<select id=\"shapeSelect_" + rowCount + "></td>"
+ "<option onclick=\"sphereSelect()\" value=\"sphere\">Sphere</option>"
+ "<option onclick=\"cylinderSelect()\" value=\"cylinder\">Cylinder</option>"
+ "</select>"
+ "</td>"
+ "<td><input type=\"text\" id=\"altitude" + rowCount + "\"</td>"
+ "<td><input type=\"text\" name=\"maxAlt\" id=\"maxAltitude_" + rowCount + "></td>"
+ "</tr>"
I need maxAltitude to become disabled for input when sphere is selected. When cylinder is selected, it should become enabled for input.
Every example I find is pretty simple but requires knowing exactly what the ID is, where in my code it is dynamically generated. This is an example of what I'm finding:
$(#maxAltitude).prop("disabled", true);
How can I do this when maxAltitude will be something more like: maxAltitude_10? There may be 1-n rows in a table, and I need to specifically disable the max altitude in the row where the dropdown select was changed.
I've tried jQuery and javascript but can't seem to find a good way to do this:
<option onclick="shapeSelect()" value="sphere">Sphere</option>
<option onclick="shapeSelect()" value="cylinder">Cylinder</option>
function shapeSelect() {
var shapeSelects = document.getElementsByName("shapeSelect");
var maxAlts = document.getElementsByName("maxAlt");
for(var i = 0; i < shapeSelects.length; i++) {
switch(shapeSelects[i].value) {
case "sphere":
maxAlts[I].disabled = True;
break;
case "cylinder":
maxAlts[i].disabled = False;
}
}
}
With the above code I get: SyntaxError: unexpected token: identifier whenever shapeSelect() is fired.
I've modified the code as follows:
<table class="myTable" id="myTable"></table>
$(table).find('tbody').append("<tr>name=\"tableRow\"</tr>"
+ "<td>"
+ "<select id=\"shapeSelect_" + rowCount + "></td>"
+ "<option value=\"sphere\">Sphere</option>"
+ "<option value=\"cylinder\">Cylinder</option>"
+ "</select>"
+ "</td>"
+ "<td><input type=\"text\" id=\"altitude_" + rowCount + "\"</td>"
+ "<td><input class=\"maxAltitudeInput\" type=\"text\" id=\"maxAltitude_" + rowCount + "\" disabled></td>"
+ "</tr>"
$('#myTable').on('change','.shapeSelector',function(){
var shouldDisableInput = $(this).val() === 'sphere';
$(this).closest('tr').find('.maxAltitudeInput').attr('disabled',shouldDisableInput);
}
And still nothing happens when I change the shape selector dropdown.
EDIT:
Apologies on the naming mismatches. My dev machine is on an airgapped network and I was hand jamming the post here on Stack Overflow. The rowCount variable was being created and incremented in another function. I was trying to only put relevant code in the post for brevity.
I was missing a class from shapeSelector. That was the missing link. It works now!
jQuery actually makes this really easy by binding this to whichever element triggered an event.
For instance, instead of writing a generic function for when that value changes, you could use jQuery to bind an event listener to them:
$('#myTable').on('change','.shapeSelector',function(){
var shouldDisableInput = $(this).val() === 'sphere';
$(this).closest('tr').find('.maxAltitudeInput').attr('disabled',shouldDisableInput);
}
You'll notice a few things in this snippet:
The element we are binding the listener to is the table, not the individual row. That's because the row is dynamic, and we don't want to have to keep adding listeners every time we add a row. Instead we add it to the parent which is stable, but then we specify that we are interested in its children that match ".shapeSelector"
The listener relies on class names, not IDs, since we want to match multiple copies of them, not just a specific one. So you'd need to add those class names or a similar way of matching more than one item
Inside the callback function that runs, you'll notice a couple uses of this. jQuery has bound that to the element that triggered the event listener, in this case, the <select> control. So when we use this, we have to think of it from that perspective. We can get its value by $(this).val(), we can find its parentt with $(this).parent(), etc. In this case, I'm travelling up to the nearest tr, then from there down to that tr's input that I want to disable. You'd need to adjust a little depending on your dom.
Also note that this is a DOM element, not a jQuery result. That's why when we want to run more jQuery commands on it, we have to put it in $() again.
That's how I'd approach it. We don't have your entire code here, so you'll have to adjust a bit, but hopefully that pushes you off in the right direction.
EDIT
To be honest, there were a lot of naming mismatches and things that didn't line up. For instance, you were attempting to append onto a tbody tag, but that tag didn't exist. You were using a rowCount variable, but didn' ever set that up or increment it. The select tag sill didn't have the class name you were trying to use.
I suggest you look at your code piece by piece, ask yourself what you're telling the browser to do, and then do that instruction in your mind to make sure the computer can do it.
HTML:
<table class="myTable" id="myTable"><tbody></tbody></table>
JavaScript:
var rowCount = 0;
function addRow(){
$('.myTable tbody').append(`<tr name="tableRow">
<td>
<select class="shapeSelector" id="shapeSelect_${rowCount}">
<option value="sphere">Sphere</option>
<option value="cylinder">Cylinder</option>
</select>
</td>
<td><input type="text" id="altitude_${rowCount}" /></td>
<td><input class="maxAltitudeInput" type="text" id="maxAltitude_${rowCount}" disabled></td>"
</tr>`);
rowCount++;
}
$('.myTable').on('change','.shapeSelector',function(){
var shouldDisableInput = $(this).val() === 'sphere';
$(this).closest('tr').find('.maxAltitudeInput').attr('disabled',shouldDisableInput);
});
addRow();
addRow();
addRow();
https://jsfiddle.net/32vnjq81/

Pass javascript variable through html code in Javascript

I have a JavaScript within which am dynamically creating an HTML form with a Confirm button. When clicking on the confirm button, certain JavaScript variables need to be passed to another JS method within the same file. But am getting an error in the code that I've written that doesn't adhere to the same js file but another jsp file, though it doesn't directly link or is affected by it in any way.
JS Function with the Button onclick
if (dropElem == "stream ui-draggable ui-draggable-handle") {
var newAgent = $('<div>');
jsPlumb.addEndpoint(newAgent,connectorProperties);
newAgent.attr('id', i).addClass('streamdrop');
var elemType = "table";
$("#container").addClass("disabledbutton");
$("#toolbox").addClass("disabledbutton");
$('#container').append(newAgent);
$("#lot").append(
"<h4>TABLE</h4></br>"+
"<h5> Select a Table </h5></br>"+
"<div class='col-md-12'>"+
"<select id='attr-combobox-style' name='attr-combobox-style' class='form-control' id='tableListDropDownCombo'>"+
"<option value='Select an option'>Select an option</option>"
);
for(var q=0;q<StreamArray.length;q++)
{
$("#attr-combobox-style").append(
"<option value="+StreamArray[q][0]+">"+StreamArray[q][0]+"</option>"+
"</select>"+
"</div>"
);
}
$("#lot").append(
"<div class='form-group'></br>"+
"<label class='col-md-12 control-label' for='tableFormConfirmButton'></label></br>"+
"<label class='col-md-12 control-label' for='tableFormConfirmButton'></label></br>"+
"<label class='col-md-12 control-label' for='tableFormConfirmButton'></label></br>"+
"<label class='col-md-2 control-label' for='tableFormConfirmButton'></label>"+
"&nbsp<button id='tableFormConfirmButton' name='tableFormConfirmButton' class='btn btn-primary' onclick='storeTableFormInfo("+newAgent+","+i+","+e+","+mouseTop+","+mouseLeft+","+elemType+")'>Confirm</button>"+
"&nbsp&nbsp<button id='tableFormCancelButton' name='tableFormCancelButton' class='btn btn-danger'>Cancel</button>"+
"</div></br>"
);
$("property").show();
$(".toolbox-titlex").show();
$(".panel").show();
i++; //Increment the Element ID for the next dropped Element
finalElementCount=i;
}
Error on Console
Since I didn't make any changes to any other files, I'm guessing that the error caused here is due to the syntax line where I've tried to pass the multiple javascript variables(newAgent,i,e,mouseTop,mouseLeft,elemType) in the onclick function of the Confirm button.
Any suggestions in this regard will be highly appreciated.
You need to declare javascript global variable and need to define first in script tag like below
<script type='text/javascript' >
var myGlobalVariable = false;
</script>
<script type='text/javascript' src='js/Other.js'></script>
...
<script type='text/javascript' >
// other java script code that depend on Other.js
</script>

How to reinitialize a script in an HTML document?

I have a document that uses the jscolor.com library, for the user to be able to select and store a color. I'm also using a JQuery function to add rows to the screen, so the user can create and define a number of colors. The problem is, when the new row is added, the Javascript isn't re-initialized for the added elements.
Here is the code in question:
<script type="text/javascript">
$(document).ready(function(){
var i=1;
$("#add_row").click(function(){
$('#addr'+i).html("<div id='addr" + i + "'>" +
"<div class='col-xs-4'>" +
"<input type='text' name='config_color[" + i + "][css]' id='input-color[" + i + "][css]' class='form-control' />" +
"</div>" +
"<div class='col-xs-2'>" +
"<input type='text' name='config_color[" + i + "][value]' id='input-color[" + i + "][value]' class='form-control jscolor' />" +
"</div>" +
"<div class='col-xs-2'>" +
"<input type='text' name='config_color[" + i + "][default]' id='input-color[" + i + "][default]' class='form-control' />" +
"</div>" +
"<div class='col-xs-4'>" +
"<input type='text' name='config_color[" + i + "][notes]' id='input-color[" + i + "][notes]' class='form-control' />" +
"</div>" +
"</div>");
$('#tab_logic').append('<div id="addr'+(i+1)+'"></div>');
i++;
});
$("#delete_row").click(function(){
if(i>1){
$("#addr"+(i-1)).html('');
i--;
}
});
}).trigger('change');
</script>
I've made an simplified example of what I'm talking about on JSFiddle - you can see in the first row, if you click in the color cell, it gives you a pop up color palette.
If you add additional rows, the popup picker doesn't work.
However, all of the data stores in the database properly, so i have an instance where some elements added by Javascript work properly and others don't?
(Also full disclosure, I asked on Reddit first - this is therefore a cross-post.
In their examples, jscolor has one called "Instantiating new Color Pickers" which shows you how to do it.
You're adding the new row as a string, which I wouldn't recommend, because if you created each input separately using jQuery it would be easier to call jscolor() on only one element, but this works too.
Just add the following to your click handler:
// Get all the inputs first
var allInputs = $('.jscolor');
// From there, get the newest one
var newestInput = allInputs[allInputs.length - 1];
// And call jscolor() on it!
new jscolor(newestInput);
Here's an updated fiddle
Generally Abe Fehr answer helped me too, but i had slightly other problem. My elements already had default values from database so
new jscolor(newestInput);
initialized them but with default FFFFF
So in my case twig (html) looks like this:
<button class="jscolor {value:'{{ category.color }}'} btn btn-sm disabled color-picker" data-color="{{ category.color }}"></button>
And then I reinitialize all the colors like this:
let all_pickers = $('.color-picker');
if ($(all_pickers).length !== 0) {
$(all_pickers).each((index, element) => {
let color = $(element).attr('data-color');
new jscolor(element, {'value': color});
});
}

Javascript - Dynamically added tabs not seen as tabs in Kendo panelbar

Ok, first off I read in an encrypted file as my DataSource, which is then converted to an XML String.
The data displays properly in my grids, except that the panels which are dynamically added to the panelbar does not seem to act as such as seen in this fiddle.
They are added to:
<ul id='panelbar'>
<li id='patDet' class='k-state-active'>
<span class='k-link k-state-selected'><input type='checkbox' id='cPatientDetails' /><label for='cPatientDetails'><a href='#' id='cbSelect'></a>Patient Detail</label></span>
<div id='patTab'></div>
</li>
</ul>
like so:
$("<li id = '"+ liID +"' class='k-item k-state-default' role='menuitem' aria-expanded='false' aria-hidden='true'><span class='k-link k-header'><input type='checkbox' id='c" + x + "' class='cbSelect' /><label for='c" + x + "'><a href='#' id='cbSelect''></a>" + liTitle + "</label></span></li>").appendTo("#panelbar");
$("<div id = 'gridGenerate" + x + "' width='400px;' aria-hidden='true'></div>").appendTo("#" + liID);
The reason for the span and link is so that styling can be used on my checkbox which can be found in this fiddle.
At first I used a hard coded DataSource, which worked perfectly, but when I switched over to fetching the data using a request, where all the data displays as it should, except for the panelbar.
This is what it looks like:
when only the first tab should be open. I created the panelbar like so:
$("#panelbar").kendoPanelBar(
{
expandMode: "single"
});
EDIT
I've made it now that the panelbar and grids are only created once the data is retreived and converted, but the issue remains.
Any idea why this is happening?
When KendoUI adds a tab, it does much more than just adding HTML tags. That's why you have methods for adding tabs on demand.
Instead of adding the HTML by hand try using:
var panelbar = $("#panelbar").data("kendoPanelBar");
panelbar.append([
{
text: "<label for='c" + x + "'>" +
"<a href='#' id='cbSelect''></a>" +
"" + liTitle + "" +
"</label>",
encoded: false,
content: "<div>text</div>"
}
]);
Click here to see it in JSFiddle.
Answer:
It seems like when I ask a question, it helps me to find the answer. Just found it.
I created the panelbar before adding the extra panels, so I just moved the:
$("#panelbar").kendoPanelBar(
{
expandMode: "single"
});
to the end of my method. The all the content is added, then I create the panelbar.

Dynamic Checkboxes getting reset?

I have a fullcalendar displayed and a list of resources with checkboxes displayed next to their name. They checkboxes show up correctly and if i dont do anything with them they stay checked like they normally would. But, when i call fullcalendar with jquery and go to add or remove a resource, the checkboxes uncheck themselves after running the jquery/fullcalendar functions.
here is how i create the checkboxes:
$(document).ready(function() {
for(p in dsnrs){
$('#specialists').append(
'<input type="checkbox" name="designer" id="' + dsnrs[p].name +'" onChange="addOrRem(dsnrs['+p+'] )" />' +dsnrs[p].name+ '<br />');
}
});
And here are my functions for adding/removing the calendar resources
function addOrRem(spec){
//alert("Specialist: " + spec.name + ", Checked: " +document.getElementById(spec.name).checked);
if(document.getElementById(spec.name).checked==true){
remRes(spec.id);
addRes(spec);
}if(document.getElementById(spec.name).checked=false){
remRes(spec.id);
}
}
function addRes(spec) {
$('#calendar').fullCalendar( 'addEventResource', spec );
}
function remRes(id) {
$('#calendar').fullCalendar( 'removeEventResource', id);
}
Here is the relevant HTML
<div id='designersbox' style='float:left;margin-top:5px'>
<div id='specialists' onload='specList()'></div>
Add resource
Remove resource
</div>
<div id='calbox' style="width:1000px;height:900px;position:relative;float:left;margin-bottom:10px; padding:10px">
<div id='calendar' style="float:left;height:1000px;width:1000px;"></div>
</div>
I'm not really sure why the checkboxes are getting reset. If i comment out the calls to the add/remove functions and just do the alert, they work fine. Any help or clues would be much appreciated.
added a var, checked = document.getElementById(spec.name).checked;
Seemed to fix the problem. I'm not sure why it works, but replacing the if statements with checked==true/false instead of not using the variable fixed the issue.
new function:
function addOrRem(spec){
//$(":checkbox[value=designer]").attr("checked", true);
//alert("Specialist: " + spec.name + ", Checked: " +document.getElementById(spec.name).checked);
var checked = document.getElementById(spec.name).checked;
if(checked==true){
remRes(spec.id, '#calendard');
addRes(spec, '#calendard') ;
}if(checked==false){
remRes(spec.id, '#calendard');
}
}

Categories