Identify cloned DOM element - javascript

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 ?

Related

Clone div and traverse to whats inside

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>

Append method not appending to all elements

I have created my own JS library. In that i am trying to define append method like this:
append: function (els) {
var elChild = document.createElement('span');
elChild.innerHTML = els;
for(i = 0; i < this.length; i++){
this[i].appendChild(elChild);
}
}
Now i am calling this append method in my script tag of HTML page like this:
<body>
<h1 class="first_heading">hello</h1>
<h1 class="second_heading">hi</h1>
<button>Test Me</button>
</body>
<script>
dome.get('h1').append('<p>some text</p>');
</script>
But the problem is all h1 tags not appending the paragraph text. Only last h1 is appending paragraph text. Any solution?
https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild:
The Node.appendChild() method adds a node to the end of the list of children of a specified parent node. If the given child is a reference to an existing node in the document, appendChild() moves it from its current position to the new position
In other words, the same node can't appear in multiple places in a document. You have to call document.createElement('span') separately for each child you want to create.
As mentioned this this corresponds to the object in scope. In this scenario, it is the window object. Here is a simple way to append the string to all headers
function append(tagname, text) {
// get all tag names
all_t = document.getElementsByTagName(tagname);
// loop through and append the string to the inner html of each tag
for (var x = 0; x < all_t.length; ++x) {
all_t[x].innerHTML += text
}
}
append('h1', '<p>some text</p>')
<h1 class="first_heading">hello</h1>
<h1 class="second_heading">hi</h1>
<button id="b">Test Me</button>

Why is JQuery on() event handler not catching events from dynamically generated elements using contenteditable?

I am dynamically generating <p> tags inside of a <div contenteditable=true> but the event handler I have setup to catch the keyup events coming from the <p> tags is not catching them.
HTML
<!-- Non-dynamically generated paragraph -->
<div class="note">
<p contenteditable="true"></p>
</div>
<!-- Contains dynamically generated paragraphs -->
<div class="note" contenteditable="true"></div>
<!-- Debug output -->
<div id="output"></div>
JS
$(document).ready(function() {
$(".note").keyup(function(evt) {
$("#output").append("<br/>note tag keyup");
// Surround paragraphs with paragraph tag
$(this).contents().filter(function() {
return this.nodeType === 3;
}).wrap('<p class="test"></p>');
});
$(".note").on("keyup", "p", function(evt) {
$("#output").append("<br/>p tag keyup");
});
});
Fiddle: https://jsfiddle.net/mibacode/axgccwoq/3/
The first div element demonstrates that I can successfully catch the keyup event from a paragraph tag generated on load. The second div shows that my dynamically generated paragraphs are not firing (or JQuery just can't catch) the keyup event.
Edit:
The issue/bug appears to be with how the paragraph tags are being generated and added to the DOM in this portion of the code:
$(this).contents().filter(function() {
return this.nodeType === 3;
}).wrap('<p class="test"></p>');
I believe for some the <p> tag isn't being added properly so the DOM isn't recognizing it or doesn't know it exists.
Edit 2:
I replaced the jQuery functionality which inserts the new paragraph tags with vanilla JS in hopes that might solve my issue, however it did not.
New code:
var textNode = null;
var nodes = this.childNodes;
for (var i = 0; i < nodes.length; i++)
{
if (nodes[i].nodeType === 3)
{
textNode = nodes[i];
break;
}
}
if (textNode)
{
var p = document.createElement("P");
var attr = document.createAttribute("contenteditable");
attr.value = "true";
p.setAttributeNode(attr);
p.addEventListener("keyup", function() {
$("#output").append("<br/>Special paragraph click!");
});
p.innerHTML = textNode.nodeValue;
var parent = $(this)[0];
parent.insertBefore(p, textNode);
parent.removeChild(textNode);
}
It appears it has to do with the way that JS handles events fired from elements within contenteditable elements.
The only problem is that you don't have the contenteditable attribute on the dynamic <p> tags. Just because they are contained within a contenteditable element doesn't make them editable also - they need the attribute explicitly.
In my updated fiddle, I have simplified it just to show that the dynamic tags work.

firstChild node issue for DOM traversal in Javascript

Building this basic to-do list from scratch to try and teach myself Javascript. I found out through the API that there is a firstChild function that will target the first child of a parent node.
If I have..
<div class = "parentNode">
<div id = "i0">
TEXT HERE
</div>
<div id = "i1">
</div>
</div>
Then I have some button that is designated to the function:
document.getElementById('myButton').onclick = function () {
var parentNode = document.getElementById('parentNode');
var childNode = parentNode.firstChild.innerHTML;
alert('childNode');
}
Why would this not return TEXT HERE in the alert box?
There are a few things going on here. First, you are looking for an element that does not exist
var parentNode = document.getElementById('parentNode');
is looking for an id. This can be remedied by using an id="parentNode on the element, or you can query by class name instead using querySelectorMDN
var parentNode = document.querySelector('.parentNode');
Next, alert('childNode'); will always alert the string "childNode" and not the variable childNode so that needs to be alert(childNode).
Lastly, and perhaps most interesting, is that .firstChild will get the first childNode of the set of childNodes. This can be a #text node (which it is), becuase of the whitespace used between the end of the <div class = "parentNode"> and the beginning of <div id = "i0">.
As opposed to using .firstChild, you can use children[0] which will only look at elements. Here is a snippet that shows this behavior.
document.getElementById('myButton').onclick = function () {
var parentNode = document.querySelector('.parentNode');
var childNode = parentNode.children[0].innerHTML;
alert(childNode);
}
<button id="myButton" type="button">Click To Check Node</button>
<div class = "parentNode">
<div id = "i0">
TEXT HERE
</div>
<div id = "i1">
</div>
</div>

How to get the first inner element?

So I want to get the first <a> tag in this <div>. This is really driving me nuts. Thanks for any help.
HTML
<div id="PGD" class="album" onmouseover="load(this)">
<a class="dl" href="#">DOWNLOAD</a>
</div>
Javascript
function load(dl)
{
var ID = $(dl).attr('id');
var elemnt = $('ID:first').attr('id');
}
Non-jQuery: (was not tagged with jQuery before, so I included this)
If you want to get the first child element only:
var element = document.getElementById('PGD').children[0];
If you want to get the first anchor element:
var element = document.getElementById('PGD').getElementsByTagName('a')[0];
With jQuery:
var element = $('#PGD').find('a:first');
// or, to avoid jQuery's pseudo selecors:
// var element = $('#PGD').find('a').first();
and actually your function can just be
function load(dl)
{
var element = $(dl).find('a:first');
}
Update:
As you are using jQuery, I suggest to not attach the click handler in your HTML markup. Do it the jQuery way:
$(function() {
$("#PGD").mouseover(function() {
$(this).find('a:first').attr('display','inline');
alert($(this).find('a:first').attr('display'));
});
});
and your HTML:
<div id="PGD" class="album">
<a class="dl" href="#">DOWNLOAD</a>
</div>
​See for yourself: http://jsfiddle.net/GWgjB/
$("#PGD").children("a:first")
This will give you the first child "a" tag, but not the descendents. E.g.
<div id="log">
<p>Foo</p>
Hello
Hello
</div>
Will give you : Hello
$(ID).find(':first')
See find jQuery command.
$('#PGD').find('a:first')
Actualy I've not understanding problem, so I'm trying correct your function, to make it clear for you:
function load(dl)
{
// var ID = $(dl).attr('id');
// var elemnt = $('ID:first').attr('id'); // Here is error-mast be like $(ID+':first')
var ID = $(dl).attr('id');
var elemnt = $(ID).find('*:first').attr('id');
}
I supose dl that is $('#PGD'). But child element A have not attribute id, what are you trying to find?
Also See: http://api.jquery.com/category/selectors/

Categories

Resources