Duplicating elements with Javascript and naming w/ counter - javascript

I think I've searched long enough to warrant asking this, and I hope I'm not missing something obvious, but I'm at my wits' end with this. I'm a complete JavaScript noob, and I'm having difficulty getting a script I found online to work correctly.
The project I was assigned was to make it so this form could be extended by clicking a button, and I thought I'd be able to accomplish it with HTML alone, but that doesn't seem possible. I found this script, and was able to get the duplication part of it to work:
http://www.quirksmode.org/dom/domform.html
However, the part of the script that's supposed to append a counter to the names of the fields isn't working, and therefore when the form is submitted, everything is recorded under the first form's name value. My guess is that that part of the script is trying to get the name of the wrong node, but I really don't know. Here's a shortened version of what I have. Ugly, but hopefully it gets the point across...
http://pastebin.com/nQhnXXKx
Let me know if I can clarify, and any help would be greatly, greatly appreciated!

Reorganizing the code, you could use something like this:
(function () {
"use strict";
var counter, init, addWorkshop, renameInputs, removeWorkshop;
counter = 0;
init = function () {
document.getElementById("moreWorkshops").onclick = addWorkshop;
addWorkshop();
};
addWorkshop = function () {
var clonedWorkshop, targetInsert;
counter++;
clonedWorkshop = document.getElementById("readroot").cloneNode(true);
clonedWorkshop.id = "";
clonedWorkshop.className = "";
clonedWorkshop.querySelector(".remover").onclick = removeWorkshop;
renameInputs(clonedWorkshop);
targetInsert = document.getElementById("writeroot");
targetInsert.parentNode.insertBefore(clonedWorkshop, targetInsert);
};
renameInputs = function (container) {
var children, i, j, cur, theName;
children = container.children;
for (i = 0, j = children.length; i < j; i++) {
cur = children[i];
if (cur.nodeName.toLowerCase() === "input") {
theName = cur.name;
if (theName) {
cur.name = theName + counter;
}
} else {
renameInputs(cur);
}
}
};
removeWorkshop = function () {
this.parentNode.parentNode.removeChild(this.parentNode);
};
window.onload = init;
}());
DEMO: http://jsfiddle.net/gAaxS/
Note that this is very structure-specific - for example, the this.parentNode.parentNode means that it has exactly two ancestors that you want to target. If you changed the HTML, you'd have to change the JS (which is usual).

Related

DOM does not update when element changes (VUE)

for a vue/mvc project i am making a page divided into html sections.
If the user clicks on a button a javascript function is called that changes the display properties of the sections so that only the clicked section is shown.
When the dom is created, it calls the function and correctly shows one section.
However when the button is clicked, the function is called again, but the dom does not change.
Here is the code for the created function:
created: function () {
var self = this;
var sectionElements = document.getElementsByTagName("section");
for (var i = 0; i < sectionElements.length; i++) {
self.sections.push({ isSelected: false, object: sectionElements[i] });
}
for (var i = 0; i < self.sections.length; i++) {
self.sections[i].isSelected = false;
}
this.showSelectedSection(0);
},
Here is the code of the javascript function.
showSelectedSection(index) {
for (var i = 0; i < this.sections.length; i++) {
if (i == index) {
this.sections[i].isSelected = true;
this.sections[i].object.style.display = "block";
}
else {
this.sections[i].isSelected = false;
this.sections[i].object.style.display = "none";
}
}
Does anyone know why this is happening and how i can fix it?
Any tips or help is greatly appreciated.
First of all, I don't totally get why you're using self = this in this example, seems like it's not necessary. Nevertheless that is not your problem. You're modifying an object inside an array, and you're doing this by accessing the index. Normally that would be ok, but vue is not aware of this change. Try either passing the direct reference to the object inside the array or add a deep watch to your array so vue can hear this changes and make the proper modifications to your DOM.

JS -> Accessing a variable from another function

I'm new to this site and to programming in general and I'm a bit stuck on a school assignment.
For reference here is my code I'm currently working on: Pastebin
function initChests(){
let gameChests = document.getElementById('chests');
for(let i = 0; i < 3; i++) {
let singleChest = document.createElement('img');
singleChest.src = 'images/chest-closed.png';
singleChest.alt = 'A chest'
singleChest.style.marginRight = '20px';
gameChests.appendChild(singleChest);
}
}
function initChestEventListeners() {
singleChest.addEventListener('click', chestClicked);
}
function chestClicked(e){
console.log("hello");
}
Now to my problem. What I want to do is access the variable singleChest in my initChestEventListeners function without making the variable global.
I am aware I can just put the singleChest addEventListener inside the initChests function, but that's not quite what I want to do either. I want to make it work with the function structure I have if possible.
Is this something that is possible and if so, can someone please explain what I would need to do or redirect me to a guide that could help explain the solution?
Thank you in advance!
Kind regards.
One option is to add an event listener to the parent element instead, the gameChests, and on click, if the clicked element is an img, do something with it:
function initChests() {
const gameChests = document.getElementById('chests');
for (let i = 0; i < 3; i++) {
let singleChest = document.createElement('img');
singleChest.src = 'images/chest-closed.png';
singleChest.alt = 'A chest'
singleChest.style.marginRight = '20px';
gameChests.appendChild(singleChest);
}
}
function initChestEventListeners() {
const gameChests = document.getElementById('chests');
gameChests.addEventListener('click', chestContainerClicked);
}
function chestContainerClicked(e) {
if (!e.target.matches('img')) {
return;
}
console.log("hello", e.target);
}
initChests();
initChestEventListeners();
<div id="chests"></div>
This method of watching for events that bubble up to the parent element is called event delegation.

Changing multiple different classes with javascript

I have been building a standalone web app for myself to learn about asp.net MVC4 and in so doing have been learning more about HTML 5, CSS and JavaScript. Needless to say I have learned alot and realized their is much more to learn. Before I ask any questions however I do a thorough search of this site and others to try and find things myself as I find I learn better by doing.
My issue here is just trying to find a way to simplify my JavaScript code if at all possible. I am not looking for JQuery at this time but will be looking into it in the future. For now strictly JavaScript.
Here is a portion of my JavaScript code that I am looking to truncate.
window.addEventListener('load', tap);
function tap() {
var elements = document.getElementByClassName('tap');
for ( var i = elements.length - 1; i >= 0; --i)
{
elements[i].innerHTML = '<img src="/Content/Images/tap_icon.jpg>")';
}
}
This code works great for what I intended it to do which is change the element class of 'tap' to a specific image no matter how many elements have the class name.
The problem is I have 8 different classes that I am doing this with one for each image that needs to be displayed. Shown below are 2 of the functions.
window.addEventListener('load', tap);
function tap() {
var elements = document.getElementsByClassName('tap');
for ( var i = elements.length - 1; i >= 0; --i)
{
elements[i].innerHTML = '<img src="/Content/Images/tap_icon.jpg">';
}
}
window.addEventListener('load', redMana);
function redMana() {
var elements = document.getElementsByClassName('red');
for (var i = elements.length - 1; i >= 0; --i)
{
elements[i].innerHTML = '<img src="/Content/Images/Red_Mana.jpg">';
}
}
Is there a way to bundle this all into one function or would it be better to keep them separate to maintain readability and easier updating if new file names are created for images/new classes added to the app.
Anything that would point me in the right direction would be much appreciated. Thank you all for you knowledge and assistance.
Edit:
Sorry I am coming back to this so late. I am in the midst of moving and starting a new job.
I have updated my JavaScript File to reflect the answer provided however when I run the application, only the first call fires.
Here is the full script for right now I will need to add further calls.
window.addEventListener('load', setImage);
function setImage(className, imageName) {
var elements = document.getElementByClassName(className);
for (var i = elements.length - 1; i >= 0; --i) {
elements[i].innerHTML = '<img src="/Content/Images/' + imageName + '.jpg">';
}
setImage('tap', 'tap_icon');
setImage('c2', '2cmana');
setImage('red', 'Red_Mana');
}
When I run the app the first call
setImage('tap', 'tap_icon');
fires, but then the following calls do nothing. I don't know what I am missing on this.
In order to not repeat the same code over and over you could add parameters to the function assuming it's the same function with different input:
//For Example:
function setImage(className, imageName) {
var elements = document.getElementsByClassName(className);
for (var i = elements.length - 1; i >= 0; --i)
{
elements[i].innerHTML = '<img src="/Content/Images/' + imageName + '.jpg">';
}
}
You could then call the function like this:
setImage('red', 'Red_Mana');
setImage('tap', 'tap_icon');
Since you mentioned you're looking to the future I'm proving an answer with ES6 features.
To simplify things you could abstract all similar codes and build a configuration-base code like so:
const baseImgPath = "/Content/Images/",
classImageMap = [
{ className: 'tap', imageName: 'tap_icon.jpg' },
{ className: 'red', imageName: 'Red_Mana.jpg' }
];
window.addEventListener('load', onWindowLoad);
function onWindowLoad() {
classImageMap.forEach(setImageToClass);
}
function setImageToClass(mapping) {
let { className, imageName } = mapping;
document
.getElementsByClassName(className)
.forEach(elem => elem.innerHTML = `<img src="${baseImgPath+imageName}"/>` );
}

JavaScript error with eventListener

I'm getting an error which I'm not quite sure what to make of. Anyway, before I go on to that, I found out about Unobtrusive JavaScript, at first I was just going to add an "OnClick" to my HTML but then found out that isn't a very good thing to do.
Anyway, so I did that and turned up with this code which isn't quite finished yet, but I wanted to try it out anyway before I went in and made any other changes.
window.onload = function findSubmitButton(){
var button = document.getElementsByClass("send_info").addEventListener("click", retrieveInputText());
}
function retrieveInputText(){
var inputArray = document.querySelectorAll("#container_id input[type=text]");
var finalArray;
for (var i in inputArray){
if(inputArray[i].type == "text"){
finalArray.push(i);
}
alert("done");
}
}
The error chrome's console gives me is this: Uncaught TypeError: undefined is not a functionfindInputs.js:5 findSubmitButton
There was also something I wanted to know, I want to be able to use this script with any other sort of input form, so instead of directly identifying the button for this page, I used a class identifier, this way, it works with any page. The only way there would be any issues would be if I had two buttons of the sort, as it is right now, any page with that sort of information only has one button for such procedures. I would appreciate if someone helped me out with this, I'm new to JavaScript.
getElementsByClassName(), returns an array, not an element so it does not have the addEventListener method, also you need to pass a function reference
window.onload = function findSubmitButton() {
var button = document.getElementsByClassName("send_info")[0].addEventListener("click", retrieveInputText);
}
Also you need to initialize the array var finalArray = [];
window.onload = function findSubmitButton() {
var button = document.querySelector(".send_info").addEventListener("click", retrieveInputText);
}
function retrieveInputText() {
var inputArray = document.querySelectorAll("#container_id input[type=text]");
var finalArray = [];
for (var i in inputArray) {
if (inputArray[i].type == "text") {
finalArray.push(i);
}
}
alert("done:" + finalArray);
}
Demo: Fiddle

JavaScript variables giving the correct value when stepped through using the console, but not otherwise

I have a variable in a JavaScript constructor that appears to be set to the correct value when stepped through using breakpoints. However, when run without breakpoints, the variable (supposed to be an array that I give it), comes up as an empty array in the console. I don't know whether or not using the get/set property of prototype, as described here. Also-- I'm working in webkit, so if someone could help explain to me why it isn't working there, I'd appreciate it. Thanks!
function Box(inElement){
var self = this;
this.element = inElement;
this.boxes = (function () {
var boxes = [];
for (var i = 0; i < inElement.childNodes.length; ++i) {
if (3 !== inElement.childNodes[i].nodeType) {
boxes.push(inElement.childNodes[i]);
}
}
return boxes;
})();
this.rotation = [-40,-20,0,20,40];
}
Box.prototype =
{
get rotation(){
return this._rotation;
},
set rotation(rotArray){
console.log('rotArray');
console.log(rotArray);
var thisrot;
this._rotation = rotArray;
for(var i=0; i<this.boxes.length; i++){
thisrot = rotArray.shift();
this.boxes[i].style.webkitTransform = 'rotateY(' + thisrot + 'deg) translateZ(170px)';
}
}
}
function loaded()
{
new Box(document.getElementById('area'));
}
window.addEventListener('load',loaded, true);
So, after some fiddling, I discovered that boxes.push(inElement.childnodes[i] is the problematic line. When commented out, the value comes out as expected.
You are removing all elements from your array in the loop inside of set rotation using shift. Arrays are passed by reference in JavaScript, not by value. If you want to create a copy of your array, you will have to use Array.slice:
this._rotation = rotArray.slice();

Categories

Resources