Trying to Clone one Element but multiplies items cloned - javascript

I am trying to clone elements onclick one time only but multiple items are being cloned when I click continuously multiple times then multiple items created.
I don't understand why multiple items are created continuously. I only want items from the data-id should be appended one time even if I click it should be removed the cloned item. Also when we click appended items they should be removed.
$('.item-save').click(function() {
$(this).toggleClass('productad')
window.localStorage.setItem('test' + this.dataset.id, $(this).hasClass('productad'));
});
$('.item-save').each(function() {
var id = 'test' + this.dataset.id;
if (localStorage.getItem(id) && localStorage.getItem(id) == "true") {
$(this).addClass('productad');
}
});
$('.item-save').click(function() {
var id = this.dataset.id;
$(".item-save").attr("data-id", function() {
var $button = $(this).clone();
$button.appendTo('.item-append');
});
});
.item-save {
position: relative;
display: block;
font-size: 14px;
margin: 5px;
padding: 5px;
background: #a5a5a5;
text-align: center;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<div class='item-all'>
<div class='item-save' data-id='123'>Save</div>
<div class='item-save' data-id='124'>Save</div>
<div class='item-save' data-id='125'>Save</div>
<div class='item-save' data-id='126'></div>
</div>
<div class='item-append'></div>
Here is my code JsFiddle Demo
I work with localStorage function to store some value so that is important without removing localStorage is it possible to fix this issue Any help or advice is highly appropriated

Get rid of the attr() function in your second click function.
Change this
$('.item-save').click(function() {
var id = this.dataset.id;
$(".item-save").attr("data-id", function() {
var $button = $(this).clone();
$button.appendTo('.item-append');
});
});
To this
$('.item-save').click(function() {
var $button = $(this).clone();
$button.appendTo('.item-append');
});

Related

How to automatically map a function to a newly added DOM element

I'm trying to automate calling a function (initBox) on all elements with class box on the page in vanilla javascript. I have made a functional solution except that it only applies to elements that are on the page when it is loaded. If I add an element to the DOM using javascript, the function (initBox) is not called on it.
Of course I can call the function (initBox) manually after adding the element, but I would like to automate it.
I'm looking for something similar to what jQuery does for events.
For example:
$('table').on('click', 'td', function (event) {
doSomething();
});
This event is called even if I add the TD element to the table later via javascript.
Here is my current solution:
function addBox() {
var btn = document.getElementsByTagName('button')[0];
var el = document.createElement('div');
el.classList.add('box');
el.innerText = (document.getElementsByTagName('div').length + 1);
btn.before(el);
}
function initBox(el) {
el.innerText += ' Initialized';
}
document.querySelectorAll('.box').forEach(initBox);
.box {
display: inline-block;
border: 1px solid #1f2227;
padding: 20px;
}
button {
padding: 20px;
}
<div class="box">1</div>
<div class="box">2</div>
<button onclick="addBox()">Add box</button>
The OP's problem can be solved just by the usage of a MutationObserver instance where the OP needs the callback from the list off added nodes just to initialize the very nodes which do match the OP's definition of a box.
function addBox() {
var btn = document.getElementsByTagName('button')[0];
var el = document.createElement('div');
el.classList.add('box');
el.innerText = (document.getElementsByTagName('div').length + 1);
btn.before(el);
}
function initBox(el) {
el.innerText += ' Initialized';
}
document.querySelectorAll('.box').forEach(initBox);
function initializeBoxClassDivOnly(node) {
if (node.nodeType === 1 && node.matches('div.box')) {
initBox(node);
}
}
function handleNodeInsertion(mutationList/*, observer*/) {
for (const mutation of mutationList) {
mutation
.addedNodes
.forEach(initializeBoxClassDivOnly);
}
};
const observer = new MutationObserver(handleNodeInsertion);
observer
.observe(document.body, { childList: true, subtree: true });
.box {
display: inline-block;
border: 1px solid #1f2227;
padding: 20px;
}
button {
padding: 20px;
}
<div class="box">1</div>
<div class="box">2</div>
<button onclick="addBox()">Add box</button>
Look Into Mutation Observers, they are the new way of observing dom objects, it allows you to run a function when an item is added or removed or modified in a particular DOM element, or if you want you can go old school ( meaning you add event listeners).
Mutation Observers ( Recommended Method ) https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Old School Evens https://developer.mozilla.org/en-US/docs/Web/API/MutationEvent
if you want me to write the code, let me know in the comments.

After first append javascript is not adding a cloned item for the second time

In my application I am adding cloned elements:
document.getElementById("add").addEventListener("click", function(){
let theblock = document.getElementById("theblock").cloneNode(true);
let newer = theblock;
newer.removeAttribute("id");
document.getElementById("restblocks").append(newer);
Bit if I do cloning outside scope it adds the element to html only once:
let theblock = document.getElementById("theblock").cloneNode(true);
document.getElementById("add").addEventListener("click", function(){
let newer = theblock;
newer.removeAttribute("id");
document.getElementById("restblocks").append(newer);
What could be the reason?
Because when you clone outside you only ever make a single clone, and a Node can only exist in one place in a document. If you want to avoid the query inside the click handler you can query outside, but clone inside.
let theblock = document.getElementById("theblock");
document.getElementById("add").addEventListener("click", function(){
let newer = theblock.cloneNode(true);
...
});
const theblock = document.getElementById('theblock');
const restblocks = document.getElementById('restblocks');
document.getElementById('add').addEventListener('click', function () {
let newer = theblock.cloneNode(true);
newer.removeAttribute('id');
newer.classList.remove('hidden');
restblocks.append(newer);
});
.block { width: 40px; height: 40px; margin: 4px; background-color: pink; }
.hidden { display: none; }
<button type="button" id="add">Add a block</button>
<div id="restblocks"></div>
<div id="theblock" class="block hidden"></div>

Elements lose javascript functionality after cloning

The following is the code. What I have done is made an item (the first one) and when i hover on its button, its background color changes to red and so on.
What I did next is cloned the element and appended it to a new div. The html (elements having same classes) is the same but the mouseover event doesn't work anymore.
My question is that why it did not work and how can I fix it? Also I tried to do the same by copying inner HTML to the new element but it is the same everytime.
const colorDiv = document.querySelector(".color-div");
const button = document.querySelector("button");
const mainContainer = document.querySelector(".main-container");
button.addEventListener("mouseover", function() {
colorDiv.style.backgroundColor = "red";
});
button.addEventListener("mouseout", function() {
colorDiv.style.backgroundColor = "seagreen";
});
const newItem = mainContainer.cloneNode(true);
document.querySelector(".new-container").appendChild(newItem);
.color-div {
height: 300px;
width: 300px;
background-color: seagreen;
margin: 10px;
padding: 10px;
transition: all .3s;
}
<!-- I will copy the div with main container class -->
<div class="main-container">
<div class="color-div">Hello</div>
<button>Change</button>
</div>
<!-- and append copied item to the following item -->
<div class="new-container"></div>
Events are not cloned, here's a quick fix:
const colorDiv = document.querySelector(".color-div");
const button = document.querySelector("button");
const mainContainer = document.querySelector(".main-container");
// If someone clicks on anywhere on the website THAT IS A BUTTON, then change the color.
document.addEventListener("mouseover", function(e) {
if (e.target.matches("button")) {
colorDiv.style.backgroundColor = "red";
}
});
document.addEventListener("mouseout", function(e) {
if (e.target.matches("button")) {
colorDiv.style.backgroundColor = "seagreen";
}
});
const newItem = mainContainer.cloneNode(true);
document.querySelector(".new-container").appendChild(newItem);
This should work for every clone.

Adding a div with CSS to DOM

For a project, I am building an app that gets user input, stores it in an array, and displays the input in the DOM. I did the first two parts but I am having trouble displaying it. More specifically, I can't get the CSS to show up.
I have tried .createElement() which creates a new list-item but it does not include CSS. I am starting to think I am completely going about this incorrectly. If you need more information or code let me know.
\\HTML
<div id="boxhold">
<ul>
<li>
<div class="twogrid">
<h1>Fruit Juice</h1>
<p>50</p>
</div>
</li>
</ul>
</div>
\\CSS
#boxhold {
margin: 0 auto;
ul {
list-style-type: none;
padding: 0;
li {
margin: 0 auto;
width: 408px;
height: 75px;
border: 3px solid $prime-color;
h1 {
font-family: $header-font;
font-weight: $header-weight;
font-size: 1em;
}
p {
font-family: $header-font;
font-weight: $header-weight;
}
}
}
}
\\JS
//Get Data
//Empty array for storing
var added = [];
//Get Data
var userInput = function() {
return {
name: document.getElementById('name').value,
amount: document.getElementById('amount').value
}
};
// Store Data
var newSugar = function(){
return added.push(userInput());
}
// New HTML
function newBox() {
var newLi = document.createElement('li');
var newName = document.getElementById('name').value;
var n = document.createTextNode(newName);
newLi.appendChild(n);
var newAmount = document.getElementById('amount').value;
var a = document.createTextNode(newAmount);
newLi.appendChild(a);
var boxhold = document.getElementById('boxhold').getElementsByTagName('ul')[0];
document.body.appendChild(newLi);
};
//Adding stuff
var displayData = (function() {
var addInput = function() {
var data = userInput();
var item = newSugar();
var box = newBox();
//var box = newItem();
};
var addFood = document.getElementById('addFood');
addFood.addEventListener('click', addInput);
document.addEventListener('keypress', function(event) {
if (event.keyCode === 13 || event.which === 13) {
addInput();
}
});
})(userInput, newSugar, newBox);
Welcome to Stack Overflow #nzart 👋
It looks like you're appending the newly created list item to the document's body, which means it will be added as the last element of the page. Your CSS indicates that the styles only apply to list items inside of an unordered list, so this would explain the lack of styles.
The simple fix should be to replace document.body.appendChild(newLi); with boxhold.appendChild(newLi);. I hope this helps!

Function that adds a div of a particular class

In my CSS I have a particular class for a div
div.videl
{
width: 80%;
margin: 0 auto;
background-color: #39275b;
color: white;
padding: 5px;
}
and then a function to add divs of that class:
this.addVideo = function()
{
var newVidElement = document.createElement("div");
newVidElement.class = "videl";
newVidElement.innerHTML = "<p>(" + ++this.numVids + ") <textarea class='vidtxt'></textarea></p>"
document.getElementById("vidplaydiv").appendChild(newVidElement);
}
However, for some reason, that function is not correctly applying the CSS properties when I test it out. See this page http://jaminweb.com/YoutubePlaylist.html and click the Add Another Video button. Any idea what I'm doing wrong?
className is the name of the attribute you're trying to set.
newVidElement.className = "videl";
When you don't know the property name of the HTML attribute, You can always use setAttribute, for example:
newVidElement.setAttribute('class','videl')
Change the JavaScript code as below -
this.addVideo = function()
{
var newVidElement = document.createElement("div");
newVidElement.className = "videl";
newVidElement.innerHTML = "<p>(" + ++this.numVids + ") <textarea class='vidtxt'></textarea></p>"
document.getElementById("vidplaydiv").appendChild(newVidElement);
}

Categories

Resources