This question already has answers here:
info on javascript document.createElement()
(4 answers)
Closed 12 months ago.
I want to display a new input field with a new name everytime a button is clicked. how do I achieve that? I found solutions to adding the input but none mentions adding a new name to it.
HTML:
Add a keyword
<div id="fieldContainer"></div>
Javascript:
function addKeywordFields() {
var mydiv = document.getElementById("fieldContainer");
mydiv.appendChild(document.createTextNode("<input type='text' name=''>"))
}
You should not use document.createTextNode to create an input. This will only create a text element with content specified inside it.
Instead you should create an input using document.createElement('input') and specify its type and name.
Since you need a dynamic name, you have to integrate a dynamic name generation logic. Here I used (new Date()).toISOString() since this will e unique each time. Instead you have to use your own logic.
Working Fiddle
function addKeywordFields() {
var mydiv = document.getElementById("fieldContainer");
const input = document.createElement('input');
input.type = 'text';
input.name = (new Date()).toISOString(); // Some dynamic name logic
mydiv.appendChild(input);
}
<a onclick="addKeywordFields()">Add a keyword</a>
<div id="fieldContainer"></div>
There's a ton of DOM methods designed to create, destroy, move, etc. There's generally three ways to create a DOM element programmatically:
clone an element with .cloneNode() and .append()
parse a string that represents HTML (aka htmlString) use .insertAdjacentHTML() instead of .innerHTML
create an element with .createElement() and .append()
Assigning attributes/properties to an element can be done initially with .setAttribute() once an attribute has been assigned to an element, it is technically a property. This distinction between attribute and property is blurry because methods seem to work 100% as does assigning properties. jQuery is not so flexible in this instance, .attr() and .prop() have silently failed on me when I forget about attribute/property quirks.
I rarely use methods to assign attributes, properties are terse and simple to use, here's a few common ones:
obj.id = "ID", obj.className = "CLASS", obj.name = "NAME"❉,
obj.type = "TEXT", obj.dataset.* = "*"
❉This is the property you use to set/get name attribute
I always add a <form> if there's multiple form controls. There are special features and terse syntax if you use interfaces like HTMLFormElement✤ and HTMLFormControlsCollection✼. Moreover having a <form> you can use it to listen for events for everything within it❉ and also take advantage of special form events like "input", "change", "submit", etc.
HTMLFormElement.elements collects all form controls (listed below)
<button>, <fieldset>, <input>, <object>, <output>, <select>, <textarea>.
From the .elements property, any form control can be referenced by #id or [name]. If there is a group of form controls that share a [name] they can be collected into a HTMLCollection.
References
HTMLFormElement✤
HTMLFormControlsCollection✼
Events
Event Delegation❉
Further details are commented in example below
// Reference the form (see HTMLFormElement)
const UI = document.forms.UI;
// Register form to click event
UI.addEventListener('click', makeInput);
// Define a counter outside of function
let dataID = 0;
// Pass Event Object
function makeInput(e) {
/*
"this" is the form
.elements property is a collection of all form controls
(see HTMLFormsCollection)
*/
const io = this.elements;
// Event.target points to the tag user clicked
const clk = e.target;
/*
This is the fieldset containing the inputs it's in bracket notation
instead of dot notation because it's a hyphenated property
*/
const grp = io['form-group'];
// This is the static input
const inp = io.data;
// if the clicked tag is a button...
if (clk.matches('button')) {
// ...increment the counter
dataID++;
/*
This switch() delegates what to do by the clicked button's [name].
Each case is a different way to create an element and assign a unique
[name] by concatenating the "data" with the current number of dataID
*/
switch (clk.name) {
case 'clone':
const copy = inp.cloneNode(true);
copy.name = 'data' + dataID;
grp.append(copy);
break;
case 'html':
const htmlStr = `
<input name="data${dataID}">`;
grp.insertAdjacentHTML('beforeEnd', htmlStr);
break;
case 'create':
const tag = document.createElement('input');
tag.name = 'data' + dataID;
grp.append(tag);
break;
default:
break;
}
}
};
form {
display: flex;
}
fieldset {
display: inline-flex;
flex-direction: column;
justify-content: flex-startd;
max-width: 90%;
}
input,
button {
display: inline-block;
width: 80px;
margin-bottom: 4px;
}
input {
width: 150px;
}
button {
cursor: pointer;
}
<form id='UI'>
<fieldset name='btn-group'>
<button name='clone' type='button'>Clone Node</button>
<button name='html' type='button'>Parse htnlString</button>
<button name='create' type='button'>Create Element</button>
</fieldset>
<fieldset name='form-group'>
<input name='data'>
</fieldset>
</form>
Related
I want to build a note taker app with html css and js but when i want add second note there is a problem.
let myNote = "";
let myTitle = "";
let noteInput = document.getElementById("note-input");
let titleInput = document.getElementById("title-input");
let title = document.getElementById("title");
let note = document.getElementById("first-note-p");
let addButton = document.getElementById("addButton");
let removeButton = document.getElementById("remove-button");
let newDiv = document.createElement("div");
let newP = document.createElement("p");
let newH3 = document.createElement("h3");
let newButton = document.createElement("button");
let notePlace = document.getElementById("note-place");
let button = document.getElementsByTagName("button");
let div = document.getElementsByTagName("div");
let paragrapgh = document.getElementsByTagName("p");
let head3 = document.getElementsByTagName("h3");
let tally = 0;
const addNote = () => {
myNote = noteInput.value;
myTitle = titleInput.value;
notePlace.appendChild(newDiv);
div[tally].appendChild(newH3);
div[tally].appendChild(newP);
div[tally].appendChild(newButton);
notePlace = document.getElementById("note-place");
head3[tally].innerText = myTitle;
paragrapgh[tally].innerText = myNote;
button[tally + 1].innerText = "remove";
tally += 1;
};
const removeNote = () => {
title.innerHTML = "";
note.innerHTML = "";
};
addButton.onclick = addNote;
<h1>Take your notes</h1>
<input id="title-input" onfocus="this.value=''" type="text" value="title" />
<input id="note-input" onfocus="this.value=''" type="text" value="note" />
<button id="addButton">add</button>
<div id="note-place"></div>
I use addNote function to add a new note but for second note I encounter to the following error.
Cannot set properties of undefined (setting 'innerText')
at HTMLButtonElement.addNote (notetaker.js:37:26)
Sorry for my bad English.
The main problem with your attempt is that you're selecting the elements before actually creating and appending them to the DOM and that will lead to problems because those elements that were initially selected are no longer there when a new note is added.
The fix is fairly easy, select the elements at the time you create a new note. Actually, I won't just stop here and I will happily invite you to follow along with my answer as we approach your task (of making notes and showing them in the screen) in a better approach that, i think, will be more helpful than just giving a fix.
So, here's what we're going to do, we're firstly go by tackling the task and see what are the main sub-tasks to do in order to have a working demo (with add and remove notes features):
To have a better performance, we'll select and cache the elements that we will use extensively in our task. Mainly, the element div#note-place should be cached because we're going to use many times when we add and remove notes.
The inputs, for the note title and text, the button that adds a note, those elements should be cached as well.
The main thing we will be doing is creating some elements and appending them to div#note-place so we can assign that sub-task to a separate function (that we will create). This function will create an element, add the wanted attributes (text, class etc...) then it returns that created element.
At this stage, our solution has started to take shape. Now, to create a note we will listen for the click event on the add note button and then we will have a listener that will handle the creation of the new note based on the values found on the inputs and then append that to the DOM. We will use addEventListener to attach a click event listener on the add note button (modern JS, no more onclicks!).
Now, for the remove note feature. The initial thinking that comes to mind is that we will listen for click events on the remover buttons and then do the work. This can work, but here's a better solution, Event Delegation, which basically allow us to have 1 listener set on div#note-place element that will call the remove note logic only when a remove button was clicked (see the code below for more info).
So, let's not take more time, the live demo below should allow you to easily understand what's being said:
/** cache the elemnts that we know we will use later on */
const notesContainer = document.getElementById('note-place'),
titleInp = document.getElementById('title-input'),
noteInp = document.getElementById('note-input'),
addNoteBtn = document.getElementById('add-note-btn'),
/** this class will be added to all remove note buttons This will allow us to catch clicks on those buttons using event delegation */
noteRemoverBtnClass = 'note-remover-btn',
/**
* a simple function that create an element, add the requested attribute and return the newly created element.
* tag: the tag name of the element to create (like div, h3 etc...).
* text: the text to show on the element (using textContent attribute).
* attributes: an object that holds "key: value" pairs where the keys are the attributes (like id, type etc...) and the values are the values for each attribute set on that parameter (see usage below).
*/
createElement = (tag, text, attributes) => {
const el = document.createElement(tag);
attributes = attributes || {};
!!text && (el.textContent = text);
for (let attr in attributes)
attributes.hasOwnProperty(attr) && el.setAttribute(attr, attributes[attr]);
return el;
};
/** listen for click events on the add note button */
addNoteBtn.addEventListener('click', () => {
/** create a div that will wrap the new note */
const noteEl = createElement('div');
/**
* create an "h3" for the note title, a "p" for the note text and a "button" that acts as the remove note button
* then loop through them and add them to the note wrapper that we just created
*/
[
createElement('h3', titleInp.value),
createElement('p', noteInp.value),
createElement('button', 'Remove', {
type: 'button',
class: noteRemoverBtnClass
})
].forEach(el => noteEl.appendChild(el));
/** append the entire note element (including the "h3", "p"p and "button" to "div#note-place" */
notesContainer.appendChild(noteEl);
});
/** implement event delegation by listening to click events on "div#note-place" and execute a set of logic (to remove a note) only when the clicked element is actually a remove button (thanks to "noteRemoverBtnClass" that we add to each created remove button) */
notesContainer.addEventListener('click', e => e.target.classList.contains(noteRemoverBtnClass) && e.target.parentNode.remove());
<h1>Take your notes</h1>
<input id="title-input" onfocus="this.value=''" type="text" value="title" />
<input id="note-input" onfocus="this.value=''" type="text" value="note" />
<button id="add-note-btn">add</button>
<div id="note-place"></div>
The above code sample is definitely NOT the only way to get things done, it only aims to be simple while recommending the use of some modern JS technics and logics. There always be more ways to do the task and even some better ways to do it.
I'm a HTML JS beginner.
Right now I'm doing a Meme generator project and I encounter a problem
this is my HTML code
//this is input tag
<div>
<input type="text" placeholder = 'text' class="mtext">
<input type="text" placeholder = 'text' class="mtext">
</div>
//this is my div block.
<div id = "meme">
<div class="mtext1" style = 'left: 5px; top: 5px; width: 400px; height: 25px'></div>
<div class="mtext1" style = 'left: 5px; top: 5px; width: 400px; height: 25px'></div>
</div>
and my thought is wanting to use "addEventListener" to solve this problem.
const inputText = document.querySelectorAll('.mtext')
const showTextBox = document.querySelectorAll('.mtext1')
inputText.addEventListener('????' , function(){
showtextbox.textContent +=
})
the first problem is what addEventListener parameter is proper to meet my expectation? That I can type into the text box and shows the result on div box at the same time.
the second problem is my inputText and showTextBox are array-like value, how can I extract the value for each of inputText and represent to the right showTextBox?
Thank you
First of all, you are looking for the change event. Check this website
// this code is wrong, read below.
inputText.addEventListener('change' , function(){
// code
});
second, inputText and showTextBox are not what you think they are.
document.querySelectorAll gives you a NodeList which is just a list of html elements (for example - [elem1, elem2...] ). See this website. So inputText and showTextBox are lists.
You need to put an eventListener to every one of those elements in the list:
inputText.forEach(element => {
// add eventListener to every element in the list:
element.addEventListener('change', function () {
// element.value gives the value inside your input elements.
// your code
})
});
The code above puts change eventListener to every mtext class.
Here is how you do it:
const inputText = document.querySelectorAll('.mtext');
const showTextBox = document.querySelectorAll('.mtext1');
//element is current element, index is the current element's index
inputText.forEach((element, index) => {
// add eventListener to every element in the list:
element.addEventListener('change', function (e) {
// element.value gives the value inside your input elements.
showTextBox[index].innerText = element.value
})
});
Here is the demo
you can also use keyup, but as this post discusses:
The reason you should not use keyup() is because if a user inputs a value using autofill, it will not fire the keyup() event. However, autofill does fire the change() event, and your verification script will run, and the input will be verified.
I want to replace a specific div element with a different one, when it has reached 3 clicks on it. That is the only task, I am trying to accomplish with the code.
I have tried looking at some code that does this but all of them replace it with get go, they don't give you a number amount to specify when to replace it with.
Example: <div id="1"></div> has been clicked on 3 times by a user. Once it exceeds that amount replace it with <div id="3"></div>
Changing the id attribute is not a good idea, instead you can use data- attribute like the following way:
var count = 0; // Declare a variable as counter
$('#1').click(function(){
count++; // Increment the couter by 1 in each click
if(count == 3) // Check the counter
$(this).data('id', '3'); // Set the data attribute
console.log($(this).data('id'));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="1" data-id="1">Click</div>
You could write a JavaScript function that keeps track how often you clicked on a specific DOM element (i. e. the div element with id="1"). As soon as the element was clicked three times, it will be replaced by another DOM element which can be created in JavaScript as well.
var clicks = 0;
function trackClick(el) {
clicks++;
if(clicks === 3) {
var newEl = document.createElement('div');
newEl.textContent = 'Div3';
newEl.id = '3';
el.parentNode.replaceChild(newEl, el);
}
}
<div id="1" onclick="trackClick(this)">Div1</div>
In case you should use a library like jQuery or have another HTML structure, please specify your question to improve this code snippet so that it fits for your purpose.
The main idea is to start listening click events on the first div and count them.
The below code shows this concept. Firstly we put first div into variable to be able to create event listeners on it and also create count variable with initial value: 0. Then pre-make the second div, which will replace the first one later.
And the last part is also obvious: put event listener on a div1 which will increment count and check if it is equal 3 each time click happens.
const div1 = document.querySelector('#id-1');
let count = 0;
// pre-made second div for future replacement
const divToReplace = document.createElement('div');
divToReplace.id = 'id-2';
divToReplace.innerText = 'div 2';
div1.addEventListener('click', () => {
count ++;
if (count === 3) {
div1.parentNode.replaceChild(divToReplace, div1);
}
});
<div id="id-1"> div 1 </div>
Note that this approach is easy to understand, but the code itself is not the best, especially if you will need to reuse that logic. The below example is a bit more complicated - we create a function which takes 2 arguments: one for element to track and another - the element to replace with. Such approach will allow us to reuse functionality if needed.
function replaceAfter3Clicks(elem, newElem) {
let count = 0;
div1.addEventListener('click', () => {
count ++;
if (count === 3) {
elem.parentNode.replaceChild(newElem, elem);
}
});
}
const div1 = document.querySelector('#id-1');
// pre-made second div for future replacement
const div2 = document.createElement('div');
div2.id = 'id-2';
div2.innerText = 'div 2';
replaceAfter3Clicks(div1, div2);
<div id="id-1"> div 1 </div>
If you know, how to use JQuery, just put a click event handler on your div 1. On that handler, increment a click counter to 3. If it reaches 3, replace the div with JQuery again.
If there are multiple divs to replace, use an array of counters instead of a single one, or modify a user-specific data attribute via JQuery.
Using native JavaScript, rather than relying upon library (for all the benefits that might offer), the following approach is possible:
// A named function to handle the 'click' event on the relevant elements;
// the EventObject is passed in, automatically, from EventTarget.addEventListener():
const replaceOn = (event) => {
// caching the element that was clicked (because I'm using an Arrow function
// syntax we can't use 'this' to get the clicked element):
let el = event.target,
// creating a new <div> element:
newNode = document.createElement('div'),
// retrieving the current number of clicks set on the element, after this
// number becomes zero we replace the element. Here we use parseInt() to
// convert the string representation of the number into a base-10 number:
current = parseInt(el.dataset.replaceOn, 10);
// here we update the current number with the decremented number (we use the
// '--' operator to reduce the number by one) and then we update the
// data-replace-on attribute value with the new number:
el.dataset.replaceOn = --current;
// here we discover if that number is now zero:
if (current === 0) {
// if so, we write some content to the created <div> element:
newNode.textContent = "Original element has been replaced.";
// and here we use Element.replaceWith() to replace the current
// 'el' element with the new newNode element:
el.replaceWith(newNode);
}
};
// here we use the [data-replace-on] attribute-selector to search
// through the document for all elements with that attribute, and
// use NodeList.forEach() to iterate over that NodeList:
document.querySelectorAll('[data-replace-on]').forEach(
// using an Arrow function we pass a reference to the current
// Node of the NodeList to the function, and here we use
// EventTarget.addEventListener() to bind the replaceOn function
// (note the deliberate lack of parentheses) to handle the
// 'click' event:
(element) => element.addEventListener('click', replaceOn)
);
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
div {
border: 1px solid #000;
display: inline-block;
padding: 0.5em;
border-radius: 1em;
}
div[data-replace-on] {
cursor: pointer;
}
div[data-replace-on]::before {
content: attr(data-replace-on);
}
<div data-replace-on="3"></div>
<div data-replace-on="13"></div>
<div data-replace-on="1"></div>
<div data-replace-on="21"></div>
<div data-replace-on="1"></div>
<div data-replace-on="6"></div>
<div data-replace-on="4"></div>
References:
CSS:
Attribute-selectors ([attribute=attribute-value]).
JavaScript:
Arrow function syntax.
ChildNode.replaceWith().
document.querySelectorAll().
EventTarget.addEventListener().
NodeList.prototype.forEach().
This question already has answers here:
How can I add an unremovable prefix to an HTML input field?
(18 answers)
Closed 6 years ago.
How do I generate an input element that has a default starting value in there that is unchangeable? For example, I am looking for a number from the user but I want '333' to be already in the input text box since all inputs will start with that number. I also don't want the user to be able to change it. I need the 333 to be part of the value also rather than just being added via style since I need to do validation on it.
I'd use 2 inputs as William B suggested but I'd consider whether to use the disabled attribute or the readonly attribute. The disabled attribute won't allow the first input to be focused and the default browser styling will give it a gray background. The readonly attribute will allow it to be focused and may have a more desirable initial styling.
One possibilty using JavaScript:
nine = document.getElementById("nine");
nine.addEventListener("keydown", function (ev) {
var el = this;
var value = el.value;
setTimeout(function () {
if (el.value.indexOf("333") != 0) {
el.value = value;
}
}, 0);
});
<input type="text" value="333" id="nine" />
I'd suggest using 2 inputs that look like a single input, with the first one readonly. See this fiddle: https://jsfiddle.net/39whrqup/2/
<input readonly value="333" class="static-input">
<input class="nonstatic-input">
.static-input {
margin-right: -20px;
width: 50px;
border: 0;
}
.nonstatic-input {
border: 0;
}
When reading the user input you will have to prepend the static portion, naturally:
var userInput = document.querySelector('input.static-input').value +
document.querySelector('input.nonstatic-input').value;
I'm attempting to reuse the variable "startGame", variable through which I declare an "a" element to be appended to the "table" element, in order to test for its own presence posteriorly. The code I've written for doing so is the following:
//Helper function to set HTML Tags attributes
function setAttributes(element, attributes)
{ for (var key in attributes) { element.setAttribute(key, attributes[key]); } }
//Opening screen
var startGame = document.createElement("a");
setAttributes(startGame,
{
"style" : "float:left; width: 100%; text-align: center;",
"onclick" : "dealHands()"
});
startGame.appendChild(document.createTextNode("Play"));
var table = document.getElementById("table");
table.appendChild(startGame);
function dealHands()
{
if (table.childNodes[0].nodeValue == startGame)
{ table.removeChild(startGame); }
...
}
So far, the code fails to perceive "startGame" and nothing happens.
You cannot set the style attributes using this:
"style" : "float:left; width: 100%; text-align: center;"
they have to be set individually. But IE doesn't support changing style in this way. You need:
element.style.cssFloat = "left"; // etc.
Discussed here
Also, I wouldn't use table as an ID.
I just had to remove the ".nodeValue" from the if statement in order to make a comparison between two HTML elements instead of comparing a value with a HTML element.