I was working on a basic commment script. This basically means whatever you type into the textboxes (one for title, and another for message) and then press submit, your comment should be added to the page, similarly how youtube does it. I was planning on having a prebuilt HTML div, and just clone it for every new comment that is made (and of course changing the text to match). The problem is that I can not get my script to work. When you press submit, no element is cloned into the container. This is my issue. What is the problem?
EDIT: I updated the code and the new error is (Cannot read property 'appendChild' of null)
window.onload = () => {
const template = document.comment;
const form = document.forms.comment;
const container = document.querySelector('.container')
form.submit2.addEventListener('click', () => {
const name = form.name;
const text = form.text;
const newNode = template.cloneNode(true);
newNode.classList.remove('hidden')
container.appendChild(newNode)
})
}
.hidden {
display: none;
}
.comment-form input {
display: block;
padding: 2px;
}
<!DOCTYPE html>
<html>
<head>
<link rel = "stylesheet" href = 'style.css'>
</head>
<body>
<form name = "comment" class = "comm">
<input type = "text" maxlength = 20 name = 'name' placeholder = 'name'>
<textarea name = "text" placeholder = 'comment'></textarea>
<input type = "button" value = "submit" name = "submit2">
</form>
<div name = "comment" class = 'hidden'>
<h1>Demo</h1>
<p>Paragraph</p>
</div>
<div class = "container"></div>
<script src = "script.js"></script>
</body>
</html>
window.addEventListener('load', e => {
const template = document.querySelector('.comment-template');
const form = document.querySelector('.comment-form');
const container = document.querySelector('.container');
form.querySelector('.submit-button').addEventListener('click', e => {
const newNode = template.cloneNode(true);
newNode.querySelector('.name').innerText =
form.querySelector('.name').value;
newNode.querySelector('.comment').innerText =
form.querySelector('.comment').value;
newNode.classList.remove('hidden');
container.appendChild(newNode);
});
});
.hidden {
display: none;
}
.comment-form input {
display: block;
padding: 2px;
}
<form class="comment-form">
<input type="text" maxlength="20" placeholder="name" class="name">
<textarea name="text" placeholder="comment" class="comment"></textarea>
<input type="button" value="submit" class="submit-button">
</form>
<div class="comment-template hidden">
<h1 class="name"></h1>
<p class="comment"></p>
</div>
<div class="container"></div>
I changed the logic to use querySelector instead of the name access. Also fixed that the container was originally coming back as an array like object, instead of just one element, which is why the append child didn't work. Also added the logic to actually stick the name and comment text into the cloned node.
window.onload = () => {
let template = document.comment;
// You have two elements with a name of comment. It is being set as the
// form, and not the div that presumably you are trying to clone later.
console.log("My template", template);
// so to fix that, you can select the element by its class instead
template = document.getElementsByClassName('hidden')[0];
console.log("My fixed template", template);
const form = document.forms.comment;
console.log("My form", form);
// getElementsByClassName returns an array like object, even if there
// is only one element matched. Using [0] lets us get just a single one
const container = document.getElementsByClassName('container')[0];
console.log("My container", container);
// using the name change you said you made to have the button named
// submit2 instead of submit, we can bind the click event to it
console.log("My submit button", form.submit2);
form.submit2.addEventListener('click', () => {
let name = form.submit2.name;
console.log("My name", name);
// The "name" element is not a child of the submit button. It is a
// sibling element with it's own name
// We need to reference it instead, and grab the value
name = form.name.value;
console.log("My fixed name", name);
let text = form.submit2.text;
console.log("My text", text);
// Same issue with the text
text = form.text.value;
console.log("My fixed text", text);
const newNode = template.cloneNode(true);
console.log("My new node", newNode.outerHTML);
// As you can see, you cloned the node, but you haven't put the name
// or text on it. Need to do that before appending it to the DOM.
newNode.getElementsByTagName('h1')[0].innerText = name;
newNode.getElementsByTagName('p')[0].innerText = text;
console.log("My fixed new node", newNode.outerHTML);
newNode.classList.remove('hidden');
container.appendChild(newNode);
})
}
.hidden {
display: none;
}
.comm input{
display: block;
padding:2px;
}
<!DOCTYPE html>
<html>
<head>
<link rel = "stylesheet" href = 'style.css'>
</head>
<body>
<form name = "comment" class = "comm">
<input type = "text" maxlength = 20 name = 'name' placeholder = 'name'>
<textarea name = "text" placeholder = 'comment'></textarea>
<input type = "button" value = "submit" name = "submit2">
</form>
<div name = "comment" class = 'hidden'>
<h1>Demo</h1>
<p>Paragraph</p>
</div>
<div class = "container"></div>
<script src = "script.js"></script>
</body>
</html>
Providing an alternative answer that steps through the original logic, attempting to show the issues, and show how to fix them. Trying to stay as close to the original logic as possible.
The problem was that in my HTML elements I added whitespace. Never add spaces around the = sign. Nothing was wrong with the script itself.
Related
I have below JS code:
<script>
function CommentStyle() {
var elementAuthor = document.getElementById("author");
var elementEmail = document.getElementById("email");
var elementUrl = document.getElementById("url");
elementAuthor.classList.add("form-control ulockd-form-bps required email");
elementEmail.classList.add("form-control ulockd-form-bps required email");
elementUrl.classList.add("form-control ulockd-form-bps required email");
}
window.onload = CommentStyle;
alert("Hello! I am an alert box!!");
</script>
ffee
<style>
.form-control {
border: 1px dashed #cccccc;
}
</style>
Alert works but the class is not added.Also how can I short this code instead of add new line for every id because the class is same?
classList.add takes multiple parameters, but will not accept strings with a space in it. You can pass in multiple strings, or if you have your classes stored in a variable classes in the form "firstClass secondClass thirdClass", you can use .split(' ') to split by spaces and then use the spread operator ... to pass the array's contents into classList.add as individual arguments.
This is even simpler for this case, since each element shares the same classes:
(Edit: OP actually ran this code on every page, including those without the relevant elements, so a check was added to exit if they did not exist in the DOM.)
function CommentStyle() {
let elementAuthor = document.getElementById("author"),
elementEmail = document.getElementById("email"),
elementUrl = document.getElementById("url");
// check IDs exist
if (!elementAuthor || !elementEmail || !elementUrl) return;
let classes = "form-control ulockd-form-bps required email".split(' ');
elementAuthor.classList.add(...classes),
elementEmail.classList.add(...classes),
elementUrl.classList.add(...classes);
// for demo purposes:
let [authorClasses, emailClasses, urlClasses] = [
elementAuthor.className,
elementEmail.className,
elementUrl.className
];
console.log({
authorClasses,
emailClasses,
urlClasses
});
}
window.onload = CommentStyle;
<label for="author">Author</label><br>
<input type="text" id="author"><br><br>
<label for="email">Email</label><br>
<input type="text" id="email"><br><br>
<label for="email">Url</label><br>
<input type="text" id="url"><br>
You don't have to access each field separately by ID, just give them all the same class and loop through that class.
<div class="comment-field" id="author"></div>
<div class="comment-field" id="email"></div>
<div class="comment-field" id="url"></div>
function CommentStyle() {
var comment_fields = document.querySelectorAll(".comment-field");
comment_fields.forEach(function(el){
el.classList.add("form-control","ulockd-form-bps","required","email");
});
}
Basically, I have a simple webpage with two text fields, and a button to choose an image from the computer. What I need to happen, is for the user to pick a photo, fill in the "artist" and "text" field, press the "Add image" button. This should then add all three items to an array, and display both the image in a div, and the text in an "li" list item.
At the moment, the image works, and will display on the screen when the button is pressed, the text seems to get pushed into the array, but no matter what I do, I can't get the text to display on the web page. I also couldn't get the image to display if I turned the array into objects, which is why I've split the pushing of the text to a separate function.
Either way, whatever I try, either breaks the image display, or breaks the text display. I can't get both to display on the page. I am trying to make it so whenever a new image and text is added, it will all display one after another sort of like this:
[album cover]
[text]
[album cover]
[text]
And this would carry on down the screen as you keep adding more. Can someone please tell me where I'm going wrong with this. Thanks.
var info = {
myImages: [],
addImage: function(imageBlob) {
this.myImages.push(imageBlob);
},
addInfo: function(artist, title) {
this.myImages.push({
artist: artist,
title: title
});
},
redrawImages: function() {
var divForImages = document.getElementById('myImages');
divForImages.innerHTML = '';
this.myImages.forEach((imageBlob) => {
var img = document.createElement('img');
img.style.width = "200px";
img.style.height = "200px";
img.src = URL.createObjectURL(imageBlob);
divForImages.appendChild(img);
});
},
redrawInfo: function() {
var ul = document.querySelector('ul');
this.myImages.forEach(function (item) {
let li = document.createElement('li');
ul.appendChild(li);
li.innerHTML += item;
});
}
}
var handlers = {
addImageAndRedraw: function() {
var fileInput = document.getElementById('fileInput');
var artistField = document.getElementById('artistField');
var titleField = document.getElementById('titleField');
if (fileInput.files.length === 1) {
info.addImage(fileInput.files[0]);
info.addInfo(artistField.value, titleField.value);
info.redrawImages();
info.redrawInfo();
}
}
}
var button = document.getElementById('button');
button.addEventListener('click', handlers.addImageAndRedraw);
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>My images</h1>
<input id="fileInput" type="file" accept="image/*" multiple="false" value="Select image">
<input id="button" type="button" value="Add image and redraw">
<div>
<input id="artistField" type="text" placeholder="artist">
<input id="titleField" type="text" placeholder="title">
</div>
<hr>
<div id="myImages">
</div>
<ul></ul>
<script src="album.js"></script>
</body>
</html>
You're adding the info to the same array as the images, so it will end up like [image, info, image, info] etc.. You're better off adding an object that contains both the image and the info, and then treating it as a single object when you add the contents to the page, rather than adding the images and text in separate functions. Also, you're not clearing the info list, so it would grow exponentially.
Here's a modified example, just after tweaking the bits I mentioned above...
var info = {
myInfo: [],
add: function(imageBlob, artist, title) {
this.myInfo.push({
image: imageBlob,
artist: artist,
title: title
});
},
redraw: function() {
var divForImages = document.getElementById('myImages');
divForImages.innerHTML = '';
var ul = document.querySelector('ul');
ul.innerHTML = "";
this.myInfo.forEach((info) => {
var img = document.createElement('img');
img.style.width = "200px";
img.style.height = "200px";
img.src = URL.createObjectURL(info.image);
divForImages.appendChild(img);
let li = document.createElement('li');
ul.appendChild(li);
li.innerHTML = info.artist + " - " + info.title;
});
},
}
var handlers = {
addImageAndRedraw: function() {
var fileInput = document.getElementById('fileInput');
var artistField = document.getElementById('artistField');
var titleField = document.getElementById('titleField');
if (fileInput.files.length === 1) {
info.add(fileInput.files[0], artistField.value, titleField.value);
info.redraw();
}
}
}
var button = document.getElementById('button');
button.addEventListener('click', handlers.addImageAndRedraw);
<h1>My images</h1>
<input id="fileInput" type="file" accept="image/*" multiple="false" value="Select image">
<input id="button" type="button" value="Add image and redraw">
<div>
<input id="artistField" type="text" placeholder="artist">
<input id="titleField" type="text" placeholder="title">
</div>
<hr>
<div id="myImages"></div>
<ul></ul>
I am currently trying to add a button, that when I click it - a list of questions appear (with textareas/inputs), every textarea/input has his own ID and I'm trying to get all of the answers printed into one form.
<h5>QUESTION BULK 1/h5>
<div id="question1">
<h4">#1</h4>
<p>Relative name?: <input id="a_relative">
<p>Age: <input id="a_age">
<p>What makes him your relative?: <input id="a_what">
</div>
I'm trying to get a button to make it add that bulk of questions, so the user can add the relevant amount.
e.g:
<button onClick="clickMe()">Add</button>
All of the data from the inputs should be later taken and put into a certain "answer sheet" form.
e.g:
function clickMe() {
var relative = document.getElementById("a_relative").value;
var age = document.getElementById("a_age").value;
var what = document.getElementById("a_what").value;
var generatedForm = " Name: "+relative+"\n\
Relative age: "+age+"\n\
Reason he/she is your relative: "+what+".";
document.getElementById("X").value = clickMe // put everything in a textarea of example,
}
Try using JQuery it makes id that you do not have to use
document.getElementById("a_relative").value;
Here's a quick solution using modern JS methods.
// Use an array to contain the questions
const questions = [
// Each question has an id, question text, and an
// object containing the input information
{ id: 1, text: 'Name', input: { el: 'input', type: 'text' } },
{ id: 2, text: 'Age', input: { el: 'input', type: 'number' } },
{ id: 3, text: 'How', input: { el: 'textarea' } },
];
// Grab and cache the elements from the page
const main = document.querySelector('.main');
const form = document.querySelector('form');
const output = document.querySelector('#output');
const add = document.querySelector('.add');
const show = document.querySelector('.show');
// Add event listeners for the buttons
add.addEventListener('click', handleAdd, false);
show.addEventListener('click', handleShow, false);
// When add button is clicked...
function handleAdd() {
// ...produce html for each question in the array...
const html = questions.map(question => {
return createQuestionHTML(question);
}).join('');
// ...and add it to the form.
form.insertAdjacentHTML('beforeend', html);
}
function createQuestionHTML(question) {
// Grab the properties/values from the question object
const { id, text, input: { el, type } } = question;
// Based on the input type choose whether an input
// or a textarea should be displayed.
// Ensure they have an answer class, and a data attribute
// with question text
let input = type === 'input'
? `<${el} type=${type} class="answer" data-to="${text}" />`
: `<${el} class="answer" data-to="${text}"></${el}`;
// Return a section containing a heading of the question text,
// and the appropriate input element
return (
`<section class="question">
<h5>${id} - ${text}</h5>
${input}
</section>`
);
}
// When the show button is clicked...
function handleShow() {
// ...select all the elements with the answer class within the form
const answers = form.querySelectorAll('.answer');
// Hide the form
main.classList.add('hidden');
// Log the value and data-to attribute text to console
[...answers].forEach(({ value, dataset }) => {
output.insertAdjacentHTML('beforeend', `<div>${dataset.to} => ${value}`);
});
}
form, .buttons { margin-bottom: 1em; }
h5 { margin-bottom: 0.2em; }
.hidden { display: none }
<section class="main">
<form></form>
<section class="buttons">
<button class="add">Add questions</button>
<button class="show">Show answers</button>
</section>
</section>
<div id="output" />
How can I add JavaScript to a single button as to copy text to the clipboard from multiple HTML inputareas including fixed text, while inserting a line break between each field?
To give you a better idea, it's simply a webpage that will allow us at work to take very repetitive notes that we always write (same points) made of 10 points, and with a click it'll copy the fields and the text that refers to the input in a form that is ready to be pasted anywhere.
<!DOCTYPE html>
<head>
<title>Repeater</title>
</head>
<body>
<button id = "button" onclick = "myFunction()">CLICK ME!</button>
<input class = "text" name = "text" type = "text" id = "textone">
<input class = "text" name = "textone" type = "text" id = "texttwo">
<input class = "text" name = "texttwo" type = "text" id = "textthree">
<input class = "text" name = "textthree" type = "text" id = "textfour">
<input class = "text" name = "textfour" type = "text" id = "textfive">
<script>
var total;
function myFunction(){
var inputarray = document.getElementByClass("text);
for(var i = 0;i < inputarray.length; i++){\
var now = inputarray[i].value;
total = total + now;
total = tatal + "____________________________________________________________________________"
}
total.select();
document.execCommand("Copy");
}
</script>
</body>
</html>
Try this
My code has two sections: a section to input new items, and a section to interact with them. I'm trying to update the dropdown list every time a new item is added with jQuery, but my current method does nothing. By nothing, I mean that the dropdown list would remain empty. I've tried previous answers to this question, but none worked. (I'm pretty new to Javascript, so me just being a noob is completely possible).
Here's the html:
<!DOCTYPE html>
<head>
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<link rel = "stylesheet" type = "text/css" href = "deletion.css"></link>
<script src = 'chemical.js'></script>
</head>
<body>
<form id = "newChemicalForm">
<p id = newChemicalText> Submit new chemicals here: </p>
<input type = "text" id = "newChemicalInput" onfocus = "this.select()" placeholder = "Enter new chemical here"/>
<button id = "newChemicalButton" onclick = "addChemical()" > Submit </button>
</form>
<form id = "newUsageForm">
<p id= "newUsageText"> Name your chemical and a usage amount. Check if the usage is daily. </p>
<select id = "chemicalDropdown">
</select>
<input type = "text" id = "newUsage" placeholder = "Ex: 250"/>
<input type = "checkbox" id = 'dailyCheckbox'/>
<p id = "dateText"> Enter the end date below: </p>
<input type = "date" id = "dateInput"/>
<button id = "newUsageButton" onclick = "addUsage()"> Submit </button>
</form>
</body>
And the Javascript:
chemicals = [];
function addChemical() {
var chemical = new Chemical();
chemicals.push(chemical);
$('#chemicalDropdown').append('<option value = "' + chemical.name + '"> ' + chemical.name + '</option> \n');
}
function Chemical() {
this.name = $('#newChemicalInput').val();
this.amount = 0;
this.usages = [];
}
There are a couple of things going on. First of all, When you press the submit button it tries to submit the first form. Second: It seems like the onclick event is not binding to the method which should add the item to the dropdownlist.
I've updated a couple of things:
Added $(document).ready(...); as it is best practice.
I've removed the inline onclick and bind the click event via jQuery.
JSFiddle here...
<form id = "newChemicalForm">
<p id = newChemicalText> Submit new chemicals here: </p>
<input type = "text" id = "newChemicalInput" onfocus = "this.select()" placeholder = "Enter new chemical here"/>
<button type="button" id = "newChemicalButton" > Submit </button>
</form>
<form id = "newUsageForm">
<p id= "newUsageText"> Name your chemical and a usage amount. Check if the usage is daily. </p>
<select id = "chemicalDropdown">
</select>
<input type = "text" id = "newUsage" placeholder = "Ex: 250"/>
<input type = "checkbox" id = 'dailyCheckbox'/>
<p id = "dateText"> Enter the end date below: </p>
<input type = "date" id = "dateInput"/>
<button id = "newUsageButton" onclick = "addUsage()"> Submit </button>
</form>
and the JS:
$(document).ready(function(){
var chemicals = [];
$("#newChemicalButton").on("click",function(){
addChemical();
});
function addChemical() {
var chemical = new Chemical();
chemicals.push(chemical);
$('#chemicalDropdown').append("<option value=" + chemical.name + ">" + chemical.name + "</option>");
}
function Chemical() {
this.name = $('#newChemicalInput').val();
this.amount = 0;
this.usages = [];
}
});
Possible the "\n" behind the append code make this not working. Try to remove it.