Clone div and traverse to whats inside - javascript

I have a div with an input inside it, i want to clone this div on click with different id, and i want the id of the input inside it to change also, and i want to limit the number of clones,
var div = document.getElementById('clonedDiv'),
clone = div.cloneNode(true);
clone.id = "some_id";
document.body.appendChild(clone);
<div id="clonedDiv">
<p>Clone this div</p>
<input type="file" id="clonedInput">
</div>
<br>
<button type="button">Clone me</button>
how can i do that? here is my code:

I'd like to offer a native JS solution to your problem. It is rather straight forward and works in all modern browsers.
outerHTML is IE4+, see here
insertAdjacentHTML is IE4+, see here
const
sourceDiv = document.getElementById('clonedDiv'),
cloneTrigger = document.getElementById('make-clone'),
maxClones = 3;
let
clonesCreated = 0;
function makeClone() {
// Make sure there aren't too many clones created.
if (clonesCreated === maxClones) {
console.log('max clones reached');
return;
}
let
// outerHTML is llke innerHTML but includes the element itself.
clone = sourceDiv.outerHTML;
// Replace the two IDs for unique IDs
clone = clone.replace('clonedDiv', `clonedDiv_${clones}`);
clone = clone.replace('clonedInput', `clonedInput_${clones}`);
// insertAdjacentHTML is like innerHTML except your can tell where it should be inserted in relation to the element.
// In this case, add the clone before the button element.
cloneTrigger.insertAdjacentHTML('beforebegin', clone);
// Increase the number of clones created.
clonesCreated++;
}
cloneTrigger.addEventListener('click', makeClone);
<div id="clonedDiv">
<p>Clone this div</p>
<input type="file" id="clonedInput">
</div>
<br>
<button id="make-clone" type="button">Clone me</button>

Since you have tagged jQuery in the question, we can use it to greatly simplify things.
Bind a click event handler to the clone button, and we can use it to call a method, say clone(), that will handle all the logic of cloning
Define a global variable, say cloneCount, that stores how many clones have been created, so that we can generate unique IDs
Clone your target <div> element.
Modify all IDs in your target element and its children (use .add() to create a superset) by simply appending cloneCount
Append cloned element to the DOM
If you want to limit the number of clones, simply track cloneCount in the method. When it exceeds a certain threshold, return to exit the function.
Here is a proof-of-concept example:
var $div = $('#clonedDiv');
var cloneCount = 0,
maxCloneCount = 5;
var clone = function() {
// Stop execution if we have cloned max number of times
if (cloneCount >= maxCloneCount)
return;
// Construct clone
var $clone = $div.clone();
// Replace all IDs (of clone and its children) to make sure it is unique
$clone.add($clone.children()).attr('id', function() {
return this.id + '_' + cloneCount;
});
// Append to DOM
$('body').append($clone);
cloneCount++;
};
$('button').on('click', function(e) {
e.preventDefault();
clone();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="clonedDiv">
<p>Clone this div</p>
<input type="file" id="clonedInput">
</div>
<br>
<button type="button">Clone me</button>

To achieve expected result, use below option
var div = document.getElementById('clonedDiv');
var count = 0;
$('button').on('click',function(){
if(count <= 3){
$('#clonedDiv').clone().attr('id','cloneDiv'+count).appendTo('body');
}
count++;
})
https://codepen.io/nagasai/pen/JrjNmq
Restrict the number clones using count variable
Using ID attribute and count, different ids can be assigned to cloned div - cloneDiv

<!DOCTYPE html>
<html>
<head>
<script data-require="rxjs#4.0.6" data-semver="4.0.6" src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div id = 'files'>
</div>
<br>
<button type="button" id = 'clonebtn'>Clone me</button>
</body>
<script type="text/javascript" charset="utf-8">
// Code goes here
window.onload = init;
function init() {
const observable = Rx.Observable.fromEvent($('#clonebtn'),'click');
observable
.take(4)
.map(1)
.scan((acc, curr) => acc + curr)
.subscribe(i=>$('#files').before(getFileTemplate(i)));
}
function getFileTemplate(i){
return `<div id='clonedDiv${i}'>
<p>Clone this div${i}</p>
<input type="file" id='clonedInput${i}'>
</div>`;
}
</script>
</html>

Related

Removing selected child from an div element

My question aims to find the simplest way for removing a child from a div element. In this case either Apple, Orange or Banana. Which means you can put the cursor on one of these words and then click delete child.
The only way Iam thinking is doing it in two steps:
Returning the index of the selected child with returnIndexOfChild() (custom built)
Using the removeChild method
I mean when you are having a big text java script engine has to loop through the whole text to find the index. Is there a more direct way like deleting anchorNode by selection object?
//html part
<div contentEditable="true" id="editableField"><b>Apple</b><i>Orange</i><b>Banana</b></div>
<button onclick="deleteChild()"> Delete</button>
//javascript part
var selection = document.getSelection();
myDiv = document.getElementById("editableField");
//Returns index of selected child (1)
function returnIndexOfChild(){
let i = 0;
for (i; i < myDiv.childNodes.length; i++){
if(selection.anchorNode.parentElement === myDiv.childNodes[i]){
console.log(i);
break;
};
}
return i;
}
//Removing Child(2)
function deleteChild (){
myDiv.removeChild(myDiv.childNodes[returnIndexOfChild()]);
}
You can just call remove() on the element itself
function deleteChild (){
const selection = document.getSelection();
selection.anchorNode?.parentElement?.remove();
}
<div contentEditable="true" id="editableField"><b>Apple</b><i>Orange</i><b>Banana</b></div>
<button onclick="deleteChild()"> Delete</button>
2 solutions:
//html part
<div contenteditable="true" id="editableField">
<li>Apple</li>
<li>Orange</li>
<li>Banana</li>
<li>potato</li>
<li>grape</li>
<li>egg</li>
<li>milk</li>
</div>
<input type="text" class="input" />
<button class="button">Delete</button>
<script>
const myDiv = document.querySelector("#editableField");
const input = document.querySelector(".input");
const btn = document.querySelector(".button");
// first way thanks to event bubbling
myDiv.addEventListener("click", (ev) => {
ev.target.remove();
});
// second way, by iterating through items
btn.addEventListener("click", () => {
// The "onclick="..." in the html is old solution, new and better is addEventListener(...)"
const myDivChildren = [...myDiv.children]; // The "..." is spread operator, "myDiv" is HTMLCollection, an array like object in orter ot iterate ovet it I created array by spreading it
const inputValue = input.value || myDivChildren.length - 1; // take input value if exist, if not then last index so remove last item
myDivChildren.forEach((el, i) => {
if (i == inputValue) {
el.remove(); // When indexes match then delete
}
return; // When indexes match then stop executing loop
});
input.value = "";
});
I got rid of getSelection(), it complicates removing items, do you care about using getSelection()? Should I use it in the answer?

How for loop work with JavaScript EventListner

I have a html document where has 3 button and one h1 element. I have tried to change the h1 element content by clicking in each button with EventListener. And i have done this too. But i dont understand how foor loop work there. How exact button working when i am click on them?
//JavaScript Code
var len = document.querySelectorAll(".mybutton").length
for (let i = 0; i < len; i++) {
var doucemnts = document.querySelectorAll("button")[i]
doucemnts.addEventListener("click", function(){
var text = this.innerHTML
document.querySelector("h1").innerHTML= "You Have Selected " + text
console.log(i)
})
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>You have selected no button</h1>
<button class="mybutton">Button 1</button>
<button class="mybutton">Button 2</button>
<button class="mybutton">Button 3</button>
<script src="index.js">
</script>
</body>
</html>
Can Anyone Tell me How For loop worked there?
For in the nodelist attach an event listener that calls a function when the button is clicked. The function updates the h1 with the button text.
Your code can be improved slightly by caching all the elements up front, and then using those references in the loop (a for/of loop is a little easier to parse) instead.
// Cache the elements
const buttons = document.querySelectorAll('.mybutton');
const heading = document.querySelector('h1');
// Loop over `buttons`
for (const button of buttons) {
// For every button add a click listener
button.addEventListener('click', function() {
// Assign the button textContent to a new variable
const text = this.textContent;
// Use that variable to update the textContent
// of the heading textContent
heading.textContent = 'You Have Selected ' + text;
});
}
<h1>You Have Selected No Button</h1>
<button class="mybutton">Button 1</button>
<button class="mybutton">Button 2</button>
<button class="mybutton">Button 3</button>
You could even use event delegation. It attaches one listener to a parent element which captures events from its child elements as they "bubble up" the DOM.
// Cache the elements, and a partial string
const heading = document.querySelector('h1');
const buttons = document.querySelector('.buttons');
const tmpl = 'You Have Selected';
// Add an event listener to the buttons container
buttons.addEventListener('click', handleClick);
function handleClick(e) {
// If the child element that fired the
// click event is a button
if (e.target.matches('button')) {
// Construct the new string, and assign it
// to the heading textContent
const text = `${tmpl} ${e.target.textContent}`;
heading.textContent = text;
}
}
<h1>You Have Selected No Button</h1>
<section class="buttons">
<button>Button 1</button>
<button>Button 2</button>
<button>Button 3</button>
</section>
Additional information
matches
Template/string literals
In JS when manipulating DOM elements, it's better to query elements once at start of your code then reuse those variables. Another important thing to consider is to use const/let instead of old var keyword. Here's your updated code based on that and might make more sense now:
const h1 = document.querySelector('h1');
const buttons = document.querySelectorAll('.mybutton');
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", function() {
const text = this.innerHTML;
h1.innerHTML = "You Have Selected " + text;
});
}
This code is basically doing the same, but instead of querying for elements on each iteration, it's done once before starting loop. Then loop over the buttons one by one and add click event to each one of them.
You can also use for..of it might be easier to understand. Here's the code updated to use for..of:
const h1 = document.querySelector('h1');
const buttons = document.querySelectorAll('.mybutton');
for (const button of buttons) {
button.addEventListener('click', function() {
const text = this.innerHTML;
h1.innerHTML = 'You Have Selected ' + text;
});
}
Update: The main thing to understand in both code snippets is const text = this.innerHTML. This line of code is using the this keyword to get the HTML content of each button. this keyword will be pointing to button triggered the event, so based on that you're getting text displayed on that button using innerHTML property and concatenate that to content of h1. In short, getting text in clicked button and append to h1 content.
I have removed console.log(i) as it refers to variable i that is valid only while add the event listener callback only. It'll show an error because when event callback function is called there will be no for loop and i won't be defined.

Adding an event listener to a procedurally assigned attribute on a single page application

I'm writing a single page note application in JavaScript with no libraries. The app lets the user add a note, and shows a shortened form beneath the input.
When they want to see the full text of their note, they can click on the relevant note to expand it.
So far, the input notes are saved in an array, then written in a shortened version (presented in HTML list elements), which have the array position assigned to them as an ID (assigning an index variable as the array length -1, then setting it as an attribute)
The view code is
<html lang="en">
<head>
<script src='./note.js'></script>
<script src='./controller.js'></script>
<title>JS Notes</title>
</head>
<body>
<h1 id="title">JS Notes</h1>
<textarea id="input"></textarea><br>
<button id="create">Create</button>
<ul id="note area"></ul>
</body>
</html>
The controller code is
window.onload = function() {
var note, createButton;
note = new Note;
createButton = document.getElementById("create")
createButton.addEventListener("click", processNote)
function processNote(){
var input, output, index, newLI;
input = document.getElementById('input').value;
note.addNote(input);
index = note.showFullNote().length - 1
document.getElementById('input').value = ""
newLI = document.createElement('li');
newLI.setAttribute("id", index)
output = input.substring(0,20)
newLI.appendChild(document.createTextNode(output + "..."))
document.getElementById('note area').appendChild(newLI)
}
The model for processing notes
(function(exports) {
function Note() {
this._list = new Array;
};
Note.prototype.addNote = function(input) {
this._list.push(input)
};
Note.prototype.showFullNote = function() {
return this._list
};
exports.Note = Note;
})(this);
I was trying to add an event listener for clicking on the list element, and passing the id of that element as the index number.
I thought this could be done by getElementsByTag, but I'm not sure how to then take the index of a specifically clicked list item, rather than the first list item on the page.
All you need to do is add the event listener to the li element before you append it to the note area.
newLI.addEventListener('click', function(e) {
let elementId = e.id;
// any other logic here
})

Identify cloned DOM element

I am using MediumEditor which a WYSIWYG editor using contenteditables. I need to assign different IDs to each element inside of the contentEditable, but (when you press Enter) the editor will clone a paragraph from an esiting one with all it's attributes. I am wondering if there is a way to identify the new <p> element from the one it was cloned from? The new element can be placed either before or after the existing one.
UPDATE:
Here's an example:
https://jsfiddle.net/wwoh7e62/
<div id="container">
<p id="myid" class="myclass" data-id="myid">some text</p>
</div>
<button onclick="doClone(); myFunc();">clone</button>
<script>
doClone = function() {
var container = document.getElementById('container');
var node = container.getElementsByTagName('p')[0].cloneNode(false);
node.innerHTML = 'cloned node';
container.appendChild(node);
}
myFunc = function () {
console.log('my func');
}
</script>
The code in doClone I don't have access to. My code should reside in the myFunc function.
While I was typing the fiddle I realized that the solution will probably be in attaching an event listener (which is not cloned) and the new node will be the one that does not have the event listener.
UPDATE:
ids that were assigned previously need to stay the same as thay are used to identify particular nodes.
You can try this :
Remove the id from "p"
<div id="container">
<p class="myclass" data-id="myid">some text</p>
</div>
<button onclick="doClone(); myFunc();">clone</button>
Then update them into your Func function :
var length = document.getElementById("container").getElementsByTagName("p").length;
for(var i=0; i < length; i++) {
document.getElementById("container").getElementsByTagName("p")[i].id = i;
}
Does this help ?

HTML list, Adding JQuery to cross selected item.

I have a little app that adds items to a list. The items appear with a button next to them, I want to be able to press that button to add a (text-decoration: line-through). I have tried a few different things but nothing seems to work (the Javascript to add items, delete the last item, add classes to the new li elements, etc. All that works fine, my problem is only with the JQuery part, more comments on the code itself).
HTML
<html>
<body>
<h1> Shopping List </h1>
<button id="add"> Add </button>
<input type="text" id="input" placeholder="Enter Items"> </input>
<button id="remove"> Remove Last </button>
<ul id="list">
</ul>
</body>
</html>
Js/Jq:
document.getElementById("add").addEventListener('click', function() {
var check = document.createElement("button");
var input = document.getElementById("input").value;
var newEl = document.createElement("li");
var newText = document.createTextNode(input);
var buttonText = document.createTextNode("Check");
newEl.className = "liEl";
newEl.appendChild(newText);
newEl.appendChild(check);
check.setAttribute("class", "checked");
check.appendChild(buttonText);
/* Problem starts here */
$("button.checked").on('click', function() {
$('li.liEl').css('text-decoration: line-through');
/* It should get the button with the class "checked" and on click, make the li elements with class "liEl" to have that css... */
}
);
var position = document.getElementsByTagName("ul")[0];
position.appendChild(newEl);
document.getElementById("input").value = "";
document.getElementById('input').onkeypress = function(e) {
if (e.keyCode == 13) {
document.getElementById('add').click(); /* adds an event listener to the submit text, keyCode 13 equals the enter key so when it's pressed it presses the add button. */
}
}
});
/* Delete last item function: */
document.getElementById("remove").addEventListener('click', function() {
var els = document.getElementsByTagName("li");
var removeEl = els[els.length - 1]; // <-- fetching last el, If els is an array, it has indices from 0 to els.length - 1. 0 is the first, els.length - 1 is the last index.
var containerEl = removeEl.parentNode;
containerEl.removeChild(removeEl);
});
Use style like $('li.liEl').css('text-decoration','line-through');
Your jQuery css function is wrong, you need to provide two parameter to set css value (see this: css-property-name-value).
Your selector syntax ($('li.liEl')) is not right, it would return all <li> element, not the one the clicked button is located.
You can use this: $(this).parent().css('text-decoration', 'line-through');.
Your code contain some bug, the last added button would not trigger the function. It is because your click function is added before the new element added to DOM. And it would cause your click function to be triggered multiple time for earlier added button.
Here's the snippet for fixed code. Since you already using jQuery, I change several native java script native element query and event handler whith jquery syntax.
$(function () {
$("#add").click(function(evt) {
var input = $('#input').val();
var check = $('<button class="checked">Check</button>');
var newEl = $('<li class="liEl"></li>');
newEl.append(input);
newEl.append(check);
$(check).click(function(evt) {
$(this).parent().css('text-decoration', 'line-through');
});
$('#list').append(newEl);
$('#input').val('');
});
$('#remove').click(function(evt) {
var lastEl = $('li.liEl').last();
lastEl.remove();
});
$('#input').keypress(function(evt) {
if (evt.keyCode === 13) {
$("#add").click();
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<body>
<h1> Shopping List </h1>
<button id="add"> Add </button>
<input type="text" id="input" placeholder="Enter Items" />
<button id="remove"> Remove Last </button>
<ul id="list"></ul>
</body>

Categories

Resources