I'm trying to get the ids of dynamically created elements, now these elements are in the form of input fields that can be modified by the user (the user enters the number of players then he gets input fields to type in their names), I'm trying to get their values as well, here is what I'm trying to do:
// Creating the input fields
const x = localStorage.getItem('playersNum');
const parentDiv = document.getElementById('player-list');
for (let i = 0; i < x; i++) {
const newInput = document.createElement("INPUT");
newInput.setAttribute("type", "text");
newInput.setAttribute("class", "form-control");
newInput.setAttribute("id", `player${i}`);
newInput.setAttribute("placeholder", "Player's Name");
parentDiv.appendChild(newInput);
}
// Trying to access them
const players = document.getElementById('player-list').children;
console.log(players.value);
There are a number of ways to go about it:
Add the inputs to an array as you create them
Add a unique class to each player input then use document.querySelectorAll('.className')
Select the inputs based on inputs with ID starting with "player"
E.g. the following just adds "Player i" as the value of each input, but it could do other things.
// Creating the input fields
// const x = localStorage.getItem('playersNum');
let x = 3;
let parentDiv = document.getElementById('player-list');
for (let i = 0; i < x; i++) {
let newInput = document.createElement("INPUT");
newInput.setAttribute("type", "text");
newInput.setAttribute("class", "form-control");
newInput.setAttribute("id", `player${i}`);
newInput.setAttribute("placeholder", "Player's Name");
parentDiv.appendChild(newInput);
parentDiv.appendChild(document.createElement('br'));
}
// Get a static NodeList of inputs with ID starting with "player"
let players = document.querySelectorAll('input[id^=player]');
// Iterate over list, adding a value
players.forEach((player, i) => player.value = 'Player ' + i);
div {border: 1px solid #aaaaaa}
<div id="player-list">
</div>
You can reduce your code somewhat by:
The default type is "text" so no need to set it
Set attributes using property access
Append inputs as they're created
so:
let newInput = parentDiv.appendChild(document.createElement("INPUT"));
newInput.class = 'form-control';
newInput.id = `player${i}`;
newInput.placeholder = 'Player\'s Name';
I'm not a fan of using a place holder as the label, much better to have an actual label and use the placeholder as a hint, so:
<label for="player0">Name player 0:<input id="player0" class="form-control"></label>
or similar.
Related
Still new to JS
I'm trying to handle a submission of a form with input elements of type radio in a way that it will trigger appending a new element to "main" element, using the value set by what the user chooses. That's the main functionality I'm looking for with this.
My code consists of 4 main js functions: three that assist creation and appendage of new elements; one that creates p elements (prGenerator), one that creates the input radio (createOption) and one that creates the form (createRadioInput), and one called foo that calls createRadioInput to which a (fifth) function is passed that has a switch used to select what new p element to append next based on input set at the point where submission handling is defined for the form when the submit button is hit.
The problem is that the switch always defaults to default, regardless of the value passed, and I don't understand why.
Here's my code, the important bits are the foo function, specially when it calls createRadioInput, and inside createRadioInput when the event listener is defined for the created form:
<body>
<div id="main">
</div>
<script>
function foo() {
const options = [];
const groupName = "group1";
prGenerator("Choose and option to render the next:");
options.push(createOption("option1", groupName, 0, "Render option 1"));
options.push(createOption("option2", groupName, 1, "Render option 2"));
const lambExec = (input) => {
console.log("pased input to lambExec: " + input);
const storyOption1 = "Option 1 selected: The quick brown fox jumps over the lazy dogs";
const storyOption2 = "Option 2 selected: A B C D E F G H I J K L M O P Q R S T U V X Y Z";
switch (input) {
case 0: //branches out to check zach story
prGenerator(storyOption1);
break;
case 1: //branches out to return to study room
prGenerator(storyOption2);
break;
default: //switch will always default to this... why?
prGenerator("...No option given.");
break;
}
};
createRadioInput("form1", groupName, options, lambExec);
}
/**#param formName unique name for the group of input radio elements
* #param groupName the name of the relevant radio input elements
* #param options list of option object (see createOptions function) that will be used to append to the main HTML element
* #param exec passed lambda to rexecute after sumition
* #desc given a name and an list of options, it will create and append the input radio elments into the main HTML element**/
function createRadioInput(formName, groupName, options, exec) {
let input = -1;
const main = document.getElementById("main"); //getting parent HTML element to append
let newForm = document.createElement("form"); //creating form
newForm.action = "#"; //form's data stays on the page
newForm.name = formName;
//this function to execute after form detects subition event
const handleSubmition = (event) => {
const group = document.getElementsByName(groupName); //get relevant group of options
for (let i = 0; i < group.length; ++i) {
if (group[i].checked) {
input = group[i].value;
console.log("submited: " + input);
}
group[i].disabled = true;
}
console.log("input before passing to exec() inside handleSubmition inside createRadioInput: " + input);
//FIXME: Why is input not working?
exec(input); //part of the story to execute after submition
event.preventDefault(); //to avoid reloading after sumition
};
newForm.addEventListener("submit", handleSubmition); //add a new event listener
options[0].input.checked = "checked"; //FIXME: maybe we don't want this... make the first option the default
//innerinner function as a lambda to apply to each element in otpions
const lambForEach = (current) => {
newForm.appendChild(current.input);
newForm.appendChild(current.label);
const br = document.createElement("br");
newForm.appendChild(br);
};
options.forEach(lambForEach); //append option
//appending the submit element
const submit = document.createElement("input");
submit.name = groupName;
submit.type = "submit";
submit.value = "Continue";
newForm.appendChild(submit);
main.appendChild(newForm);
}
/**#param id the ID for the input
* #param name name for the radio input
* #param value the value to be returned if slected
* #param text the inner text for the label of input
* #desc helper function to create user input of type radio*/
function createOption(id, name, value, text) {
//creating the input radio element
let newInput = document.createElement("input");
newInput.type = "radio";
newInput.id = id;
newInput.name = name;
newInput.value = value;
//creating the lable
let newLabel = document.createElement("label");
newLabel.for = id;
newLabel.innerHTML = text;
return {
input: newInput,
label: newLabel
};
}
/**#param str string to display on the website
* #desc automatizes the creation of pr elements*/
function prGenerator(str) {
const main = document.getElementById("main");
const pr = document.createElement("p");
pr.innerHTML = str;
main.appendChild(pr);
}
</script>
<script>
foo()
</script>
</body>
<footer>
this is a footer
</footer>
Also, if anybody has any suggestions in how to better achieve the previously described functionality, like maybe using promises and handling them(?).
Just as #Jon P explained in the comment to my question, a form returns String values, and the switch was using ints. So switch should be case "0":... case "1":, etc.
Next time, a debugger should be used instead of just printing to the console
I have a button that creates two input fields on click. I want to add a unique id for each couple of inputs that is created so that I can delete them later. Currently when I add the inputs they all have the same id 0 and the index does not increment, why and how can I make it increment? Here is my code:
createNewPricedRoundShareholder() {
const mainParent = document.getElementById('main-parent');
var index = 0;
const newPlatformNameInput1 = document.createElement("input");
newPlatformNameInput1.id = index + '_first';
newPlatformNameInput1.value = index;
const newPlatformNameInput2 = document.createElement("input");
newPlatformNameInput2.id = index + '_second';
newPlatformNameInput2.value = index;
const deleteButton = document.createElement("button");
deleteButton.innerText = 'delete';
const wrapperParent = document.createElement('div');
wrapperParent.id = index + '_parent';
wrapperParent.appendChild(newPlatformNameInput1);
wrapperParent.appendChild(newPlatformNameInput2);
wrapperParent.appendChild(deleteButton); mainParent.appendChild(wrapperParent);
index++;
}
and my html:
<div id="main-parent"></div>
I know you said you want an ID so you can use it to delete your row later, but you don't actually need it. If you add this code to your function, you can delete the entire row without the need of using an ID. This code will allow you to target the specific button, then the parent of that button and remove it.
deleteButton.addEventListener("click",function(e){
e.preventDefault();
e.target.parentNode.remove();
});
I think that is because you increase the index at the end of your function but you should increase it after creating the first input and before creating the second one
[...]
const newPlatformNameInput1 = document.createElement("input");
newPlatformNameInput1.id = index + '_first';
newPlatformNameInput1.value = index;
index++;
const newPlatformNameInput2 = document.createElement("input");
newPlatformNameInput2.id = index + '_second';
[...]
We have a customized Telerik RadComboBox that builds a text string of names separated by semicolons in the textbox field of the combobox by selecting names from the dropdown. We want to improve this and replicate a behavior similar to Outlook where individual names can be selected and deleted from the textbox. The selected names are stored in a hidden input field. Another hidden field carries a CSV list of IDs. Both hidden fields are treated like arrays, and their values have a one-to-one relationship by index. The approach we are considering is to wrap some kind of tag around each name, like a span, so that when a user selects a particular name the whole name will be highlighted and can then somehow be detected. Could this be done by using an ItemTemplate?
The OnClientSelectedIndexChanged event fires the javascript shown, and appends the list of names to the textbox.
function RecipientSelectedIndexChanged(sender, args) {
var combo = $find('<%= rcbRecipients.ClientID%>');
var userid = combo.get_selectedItem().get_value();
var name = combo.get_selectedItem()._text;
var listID = $get("<%= hdnRecipientIdList.ClientID%>").value;
var listName = $get("<%= hdnRecipientNameList.ClientID%>").value;
var listIDArray = new Array();
var listNameArray = new Array();
if (listID != '') {
listIDArray = listID.split(',');
listNameArray = listName.split('; ');
}
listNameArray.pop();
for (var i = 0; i < listNameArray.length; i++) {
listNameArray[i] = listNameArray[i] + '; ';
}
var x = listIDArray.indexOf(name);
if (x == -1) {
listIDArray.push(userid);
listNameArray.push(name + '; ');
listID = listIDArray.toString();
var y = listNameArray.join('');
listName = y;
}
$get("<%= hdnRecipientIdList.ClientID%>").value = listID;
$get("<%= hdnRecipientNameList.ClientID%>").value = listName;
combo.trackChanges();
var item = combo.findItemByText(name);
item.hide();
combo.set_text(listName);
combo.commitChanges();
combo.showDropDown();
}
I have a dynamic table which has the input field in each row. I would like to get the value entered by the user through Javascript. I am able to get the HTML code for the input field 'quantity' but not the value.
Here is the part of the javascript code :
var table = document.getElementById("mactable");
var row_count = table.getElementsByTagName("tr").length;
var grand_total = document.getElementById("grand_total");
var grand_price = 0.00;
for (var i = 1, row; row = table.rows[i]; i++)
{
var price = row.cells[4].innerHTML;
var quantity = row.cells[2].innerHTML;
alert(quantity);
if(isNumber(price))
{
var num = parseFloat(price);
grand_price = grand_price + num;
}
}
Any idea how can it be done ?
Instead of reading table cell innerHTML you can find inner input element and then read its value property:
var quantity = Number(row.cells[2].querySelector('input').value);
Here row.cells[2] is a td element. Using querySelector('input') you can find the first children input.
Also note, that since the value of the field is a string, you might want to cast it to number, for example with Number function.
I've implemented an auto-completion list in javascript so that if User types 'a', all names starting with 'a' are displayed in drop-down menu. Now I want to make the text bold depending on user input in the drop down menu. So if user types 'ab', the letters 'ab' should appear bold in drop-down menu containing the word about.
Here is some part of my JS code where I'm displaying the names:
document.getElementById('dropmenu').style.visibility='visible';
var element = document.createElement("div");
var namecontainer = document.createElement("div");
namecontainer.setAttribute('id', "name" + div_id);
namecontainer.className = "namecontainerclass";
element.setAttribute('id', "div" + div_id);
element.className = "elementclass";
var text = document.createTextNode(myArray[i].name);
element.appendChild(text);
document.getElementById('dropmenu').appendChild(namecontainer);
document.getElementById("name" + div_id).appendChild(element);
var img=document.createElement("img");
img.setAttribute("src", myArray[i].image);
img.setAttribute("width", 25);
img.setAttribute("height", 25);
namecontainer.appendChild(img);
This is something that came up my mind. You want to check the user-input against the name (myArray[i].name), at that point create some textnodes (or an element if it matches), and append those to the container element (a div in this case). I didn't test it, but it is a bit pseudocode to show how to handle this higlighting without using any javascript framework.
// searchString is the string entered by the user...
// split by the searchString, but group the selection so the matches
// also get added to the result array.
var match = new RegExp('\\('+ searchString +')\\',"gi");
var textParts = myArray[i].name.split(match);
var nodes = [];
var element = document.createElement("div");
// only create elements for names that actually match.
// if there is only one match, there wasn't a match by the split
if (textParts.length > 1){
for(i = 0; i < textParts.length; i++){
if (textParts[i] == searchString) {
nodes[i] = document.createElement("em");
nodes[i].createTextNode(searchString);
} else {
nodes[i] = document.createTextNode(textparts[i]));
}
element.appendChild(nodes[i]);
}
}