JavaScript - clone of Template with cloneNode and Json data - javascript

I want to create several elements using JSON data with a template, by creating a clone of the template and appending them to the specific div.
However, when using the cloned template variable, the clone can't be used inside the function which handles the JSON data, even when this clone is above the function (which should make it global for everything the same level?).
HTML
<template class="content">
<h2>Hello World</h2>
<h3>Hello World</h3>
</template>
<div class="box">
</div>
JS
let content = document.querySelector(".content").content;
let box = document.querySelector(".box");
doStuff();
function doStuff() {
let content = document.querySelector(".content").content;
let box = document.querySelector(".box");
//make a clone of the template
let clone = content.cloneNode(true);
// change content of H2
clone.querySelector("h2").textContent = "First Box";
// change content of H3 with data fetched from API
fetch("http://kea-alt-del.dk/t5/api/product?id=21").then(e => e.json()).then(productJson => otherStuff(productJson));
function otherStuff(productJson) {
clone.querySelector("h3").textContent = productJson.name;
}
// add the clone to the box div
box.appendChild(clone);
}
Working fiddle as Example: https://jsfiddle.net/c1x98hmh/4/
We can see in the result that the h2 content has been changed, however, h3 remains unchanged. Console log tells me that clone is null.
How come this example works when we change the template element to a div?
(I need this for Json, that's why I have a template, function, clone and inside function)

clone is a DocumentFragment object. Here's what appendChild does with a DocumentFragment:
If the given child is a DocumentFragment, the entire contents of the DocumentFragment are moved into the child list of the specified parent node.
keyword being "moved" — that's why clone is empty when otherStuff is called.

My guess is that since the <template> element is not to be rendered by the browser, its content is not part of the DOM and thus it is not possible to query it with a DOM query such as document.querySelector().
EDIT: It is confirmed here: https://www.html5rocks.com/en/tutorials/webcomponents/template/
Content is considered not to be in the document. Using document.getElementById() or querySelector() in the main page won't return child nodes of a template.

Related

Is there a way to get the exact indexOf an element in a big html string

I am working on an app with vue.js and quill.js in which I am creating some documents.
The content of a document is stored in document.content which is one giant string with a bunch of html tags in it coming straight from quill.js.
When previewing the document I'm rendering the big html string inside a div with v-html attribute like this:
<div v-html="document.content"></div>
i.e.
document.content = "<p>Hello</p><p>World</p><p>Hello World</p><p>Hello</p>"
It's rendereded as (you get the idea):
<div data-v-4ee08204>
<p>Hello</p>
<p>World</p>
<p>Hello World</p>
<p>Hello</p>
</div>
The question is:
When clicking somewhere inside the div is there a way to get the exact index of the character/word/element I've clicked on (because I need to add a comment to it)?
I've tried to attach a click listener to the div, getting the outerHTML of the target element and trying to get the indexOf document.content, but it's not always working, because there can be similar stuff inside the big string like <p>Hello</p> twice and it will get the first one only.
It's possible that my whole approach is wrong, but I'm not really sure.
Any suggestion is welcome. Thanks!
What you could do is to clone the parent element, add the comment using DOM manipulation and then use the parent element's innerHTML, here is an example:
const parent = document.querySelector('#parent');
parent.addEventListener('click', event => {
event.target.classList.add('toBeModified');
const clone = parent.cloneNode(true);
const node = clone.querySelector('.toBeModified');
const comment = document.createElement('span');
comment.textContent = '(edited)';
node.appendChild(comment);
node.classList.remove('toBeModified');
event.target.classList.remove('toBeModified');
console.log(clone.innerHTML);
});
<div id="parent">
<p>Hello</p>
<p>World</p>
<p>Hello World</p>
<p>Hello</p>
</div>
What this does is to add a class (toBeModified) to the clicked element so it can be easily found once the parent is cloned.

.appendChild() an HTML element on click

I wanted to copy an entire row including its' siblings and contents on button click. When I click the button the element, it appears in the console but doesn't append to the page. This is my code:
It doesn't show any error messages. I've tried innerHTML/outerHTML or append() it doesn't work.
$(document).ready(function() {
$('#addSubFBtn').on('click', function() {
var itm = document.getElementById("trFb");
var wrapper = document.createElement('div');
var el = wrapper.appendChild(itm);
document.getElementById("tbFb").append(el);
console.log(el);
});
});
Seems like what you're trying to do is clone the item after you get it from your document. W3schools website explains how to accomplish this. Check out the link: https://www.w3schools.com/jsref/met_node_clonenode.asp
Once you clone the node, [appendchild] should work as intended
Not sure (as said without seeing related HTML) but i see flaw in your logic:
var itm = document.getElementById("trFb");
still exist on the document(so in the page) so you've to retrieve it before you want to add/move it to another place.
using .removeElement will return you removed element(or null if no element matche the selector) so correct script should be:
var itm=document.getElementById("trFb").parentNode.removeChild(document.getElementById("trFb"));
as shown here to remove element you've to use method on to parent element.
So you can add it to any other element existing.
For more specific use or element created in global JS variable (such an createElement not yet appended) you can see :document.createDocumentFragment(); as explained here https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment

Append values to a specific element in a string with Jquery

I have an element in local storage with multiple elements, for simplicity, I will make the element:
<div id="outer">
<ul id="inner">
<li id="item">
</li>
</ul>
</div>
The element is saved as a string and I want to manipulate the contents.
Like such:
let local_storage_element = localStorage.getItem("val")
$(local_storage_element+':last-child').append("<p>something</p>")
No matter what selector I add after local_storage_element it will always append the value to the string not to the selected element(:last-child in this case)
does anyone know how to append to a specific element within the string??
Although you have written jquery in the title there is a javascript tag added also so I thought why not provide an answer that justifies your needs and helps you accomplish the task in the same way you want.
The
DocumentFragment interface represents a minimal document object that has no parent. It
is used as a lightweight version of Document that stores a segment of
a document structure comprised of nodes just like a standard document.
The key difference is that because the document fragment isn't part of
the active document tree structure, changes made to the fragment don't
affect the document, cause reflow, or incur any performance impact
that can occur when changes are made.
So how to do it as the DocumentFragment still appends node with it and not string, we will create a temp element and add the HTML from the localStorage using innerHtml and then append the firstChild of that temp node i.e our actual string which is now treated as a node, to the document fragment and then search and appends HTML to it, we will use our temp element to add HTML every time see below.
I will append a new child div to the element #outer in the string given above in the post here is the working FIDDLE as SO does not support localStorage you can see it working there open the console to view the resulting HTML with the new child added and below is the code
$(document).ready(function () {
if (localStorage.getItem('html') === null) {
localStorage.setItem('html', '<div id="outer"><ul id="inner"><li id="item"></i></ul></div>');
}
var frag = document.createDocumentFragment();
var temp = document.createElement('div');
temp.innerHTML = localStorage.getItem('html');
frag.appendChild(temp.firstChild);
temp.innerHTML = '<div class="new-child"></div>'
frag.querySelector("#outer").appendChild(temp.firstChild);
console.log(frag.querySelector("#outer"));
localStorage.removeItem('html');
});
You can't use string as selector. If you want transform string to html then you should put it in some element as innerHTML. So try create some hidden div and insert your string as HTML to it. Something like this
var your_string = '<ul><li>1</li><li>2</li><li>3</li><li>4</li></ul>';
document.querySelector('.hidden').innerHTML = your_string;
document.querySelector('ul li:last-child').innerHTML = 'your content';
document.querySelector('.result').appendChild(document.querySelector('ul'));
Example
The problem may arise when you get '<div id="outer">' from localStorage to use it as a selector since it only accepts "#outer" to be a selector. If you want to add an element to be the last child of parent's element, you could use after() instead of append().
$(document).ready(() => {
if ($("#charl").children().length === 0)
{
// if using after with no element inside ul then it will be inserted after il
$("#charl").html("<li>foo</li>")
}
else {
$("#charl li").after("<li>bar</li>")
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="charl">
<li>Foo</li>
</ul>

Append <div> using jquery's forEach and templatePlugin

I'm struggeling for some hours with the following problem:
Iterating through a object using jquery's forEach method
For each object element, a template should be used, which also fills the information in the template. The plugin, which I am using: jquery-tempalte
For each object the template should be appended to a <div>
Unfortunately only the last object element is visualized and loaded to the DOM. All other divs, which are created during the steps before, were overwritten.
Find here an jsFiddle, which demonstrates my problem: http://jsfiddle.net/7oq053od/1/
Do you have any idea, what I am missing?
Would be great to get some feedback :)
From Options section on the github webpage you can see this third parameter with the append property set as true.
Just consider add it to your script like this :
$.each(results.features, function (key, value) {
$("#homeView").loadTemplate($("#template"), {
geoFenceName: value.attributes.title,
geoFenceDescription: value.attributes.description
}, {append: true});
});
Here is a working JsFiddle.
For appending multiple div's . you should create a main container that contains all the child div's and when displaying, each div element will be created and append to main container everytime.
Here's a sample code:
<div id="container"></div>
//now in javascript create child div elements and append to main container
<script>
for(var items in data)
{
var newChild = $(document.createElement('div'));
$(newChild).css({
//some css code
});
//now appending it to main container
$('#container').append(newChild);
}
</script>
It seems the template plugin applies the templated values to the same element. If you clone the element, your script works.
$.each(results.features, function (key, value) {
$("#homeView").clone().loadTemplate($("#template"), {
geoFenceName: value.attributes.title,
geoFenceDescription: value.attributes.description
}).appendTo('#fences');
});
See updated fiddle here
The problem is that the loadTemplate function overwrites the div. You can fix that by cloning the template container and then append it to '#homeWiew'.
I created a fork from your jsfiddle that worked for me:
http://jsfiddle.net/6ksdnejw/

Save the parents children in a var to append later

I have a markup in one of my website pages as follows:
<div id="mainPage">
<div>
<div><ul><li>etc.</li></ul>
</div>
<div>
<div><ul><li>etc.</li></ul>
</div>
</div>
What the above means is that there's a main div in my website which has the content. I want to take all the children of the particular div and save it in a var, since I want to use that var later for something like $('resurrectPage').append(someVar); where someVar has the dom elements from the main page div.
How can all the children of a particular element be selected and added to a var?
$('#mainPage').html() would give you the entire thing in a string "<div>
<div><ul><li>etc.</li></ul> </div> <div> <div><ul><li>etc.</li></ul> </div>"
$('#mainPage').children() would give you immidiate children [div,div]
$('#mainPage').find('.div') would giv =e you all the divs inside it [div,div,div,div]
if #mainPage is your main div, you can get of it's children by
var someVar = $('#mainPage').children();
Official api page
I think you're looking for jQuery detach method...
http://api.jquery.com/detach/
It will remove an element and store its contents, ready to be re-appended:
var a = p = $("a").detach()
If you only need the HTML you can save the HTML: var someVar = $("#mainPage").html(); and then append the HTML with the code you already have. Please tell me if I have misunderstood your question.

Categories