I have a JS function that updates descriptions of products when variables are changed. Here is where the element is updated.
Product.Config.prototype.updateProductShortDescription = function(productId) {
var shortDescription = this.config.shortDescription;
if (productId && this.config.childProducts[productId].shortDescription) {
shortDescription = this.config.childProducts[productId].shortDescription;
}
$$('#product_addtocart_form div.short-description').each(function(el) {
el.innerHTML = shortDescription;
});
};
Works great but would like to wrap the output in a div. Does anyone know a way or wrapping innerHTML in a tag before when updating it?
Thank you.
Don't use innerHTML if you're just setting text. It is incredibly insecure and will happily execute scripts and contact any server that the text you're using can instruct the browser to do by simply including some malicious HTML code.
Use the .textContent property instead.
But better yet, since it looks like you're using jQuery anyway, just use jQuery's built-in way to construct elements as needed:
let div = $(`<div></div>`).text(description);
$(`.my-element`).append(div);
And if you already have elements:
let update = $(`<div></div>`).text(description);
$(`#your.query-selector goes:here()`).empty().append(update);
(because jQuery lets you chain calls in a way that make them apply to every element in a selection, this will set that div-wrapped description as content for every element in the query result)
I don't know what your $$ function is from, but there is my proposal in vanilla JS:
const descriptionText = "my short description";
const descriptionElements = document.querySelectorAll('#product_addtocart_form div.short-description');
Array.from(descriptionElements).forEach(function(el) {
const newDiv = document.createElement('div');
newDiv.classList.add('customDiv');
newDiv.textContent = descriptionText;
el.appendChild(newDiv);
});
div {
border: 1px dotted silver;
padding: .5em;
margin: .25em;
}
<div id="product_addtocart_form">
<div class="short-description">1</div>
<div class="short-description">2</div>
<div class="short-description">3</div>
</div>
Edit:
After reading your first comment, in your code you just have to replace:
el.innerHTML = shortDescription;
by:
const descWrapper = document.createElement('div');
descWrapper.innerHTML = shortDescription;
el.appendChild(descWrapper);
Related
Knowledge level: Beginner
What I expect from my code:
The user clicks on a class named open.
The textNode within 'open' gets replaced with a - sign.
Then I go to the first child of the parent of that class which is an h2 tag and get the title in order to place it within the sibling of 'open' named 'info'.
At last info turns visible.
The ternary operator is to check if we have only a nodeType of 3 within the firstChild. If yes get the text, if not then get the entire innerHTML.
Since I get a html collection from getElementsByClassName I tend to create a loop so that I can modify the style.
Why I do this or don't use jQuery:
I am trying to push myself and learn how to effectively manipulate the dom without third party libraries. I would appreciate hints on improving my code but please keep the basic structure the same as I am still not into advanced short cuts and I am trying to learn not copy.
Problem I am not sure how "correct" my idea of manipulating the dom is. I could not get this to work, neither do I know how to effectively tell javascript to handle only the currently clicked element.
http://jsfiddle.net/r7bL6vLy/28/
function wrapper () {
var open = document.getElementsByClassName(open);
function trigger (){
var info = this.nextSibling;
var getTitle = this.parentNode.firstChild.(nodeType == 3 ? textContent : innerHTML)
this.removeChild(textContent);
this.appendChild(document.createTextNode('-'));
info.appendChild(document.createTextNode(getTitle + 'details'));
info.style.visibility = 'visible';
}
for (i = 0; i < open.length; i++) {
open[i].addEventListener('click', trigger, false);
}
}
HTML
<div id='A'>
<h1>Stackoverflow Question</h1>
<div class='open'>+</div>
<div class='info'>Content A...</div>
</div>
<div id='B'>
<h1>Stackoverflow Question</h1>
<div class='open'>+</div>
<div class='info'>Content B...</div>
</div>
this.nextSibling will give you the textNode representing the whitespace between the elements. Use .this.nextElementSibling instead.
You don't need to do any traversal to change the + to a - since you already have the open element. Just assign it the new value.
this.textContent = "-";
To assign the h2 content, simple use .previousElementSibling.textContent and assign it to info.textContent
info.textContent = this.previousElementSibling.textContent
Some things you were doing wrong were:
using invalid syntax here:
var getTitle = this.parentNode.firstChild.(nodeType == 3 ? textContent : innerHTML)
Should have been an if statement, though the condition doesn't really seem necessary. You can use .textContent on an element too, as long as you don't need the HTML representation.
Technically you could do this:
var child = this.parentNode.firstElementChild;
var getTitle = child[child.nodeType === 3 ? "textContent" : "innerHTML"];
...but that's pretty ugly. Avoid clever tricks like this.
Using textContent as a reference to an element:
this.removeChild(textContent);
Things that could be improved:
When changing text, favor manipulating .textContent over creating new text nodes. The existing nodes are mutable and so can be reused.
If you want to copy a section of the DOM to a new location, don't use .innerHTML but instead use .cloneNode(true).
var copy = myElem.cloneNode(true);
targetElem.appendChild(copy);
Otherwise you're taking the DOM nodes, serializing them to HTML and then immediately parsing the HTML into new nodes. All that string manipulation can be avoided simply by cloning.
You almost had it:
function trigger (){
var info = this.nextElementSibling,
getTitle = this.parentNode.firstElementChild.textContent;
this.textContent = '-';
info.appendChild(document.createTextNode(getTitle + 'details'));
info.style.visibility = 'visible';
}
var open = document.getElementsByClassName('open');
for (i = 0; i < open.length; i++)
open[i].addEventListener('click', trigger, false);
function trigger (){
var info = this.nextElementSibling,
getTitle = this.parentNode.firstElementChild.textContent;
this.textContent = '-';
info.appendChild(document.createTextNode(getTitle + 'details'));
info.style.visibility = 'visible';
}
var open = document.getElementsByClassName('open');
for (i = 0; i < open.length; i++)
open[i].addEventListener('click', trigger, false);
#a, #b {
width:50%;
height:100%;
margin:auto;
}
h1 {
width:100%;
font-size:160%;
text-align:center;
}
.open {
width:22%;
margin:auto;
padding:10% 0;
line-height:0;
font-size:150%;
text-align:center;
font-weight:bold;
background:yellow;
border-radius:100%;
}
.info {
width:100%;
padding:5%;
margin:5% auto 0 auto;
text-align:center;
background:ghostwhite;
visibility:hidden;
}
<div id='A'>
<h1>Stackoverflow Question</h1>
<div class='open'>+</div>
<div class='info'>Content A...</div>
</div>
<div id='B'>
<h1>Stackoverflow Question</h1>
<div class='open'>+</div>
<div class='info'>Content B...</div>
</div>
Remember that whitespace between elements becomes a text node. So better use firstElementChild and nextElementSibling instead of firstChild and nextSibling.
Okay, I'm unsure how to word the question, but basically I want to repeat my div containers that have a class of "blocks" using only javascript, no HTML (other than the HTML needed to start a page). IF I were doing this using HTML the result should look exactly like this.
http://jsfiddle.net/nqZjB/1/
<div class = "blocks"> <!-- Repeats three times -->
However as I stated in the description I do not want to use any HTML, so here is my fiddle with javascript only.
How do I make div class blocks repeat three times as in my HTML example using only javascript? Of course in real life I would use HTML for this as javascript is unnecessary, but I want to do this in pure javascript so I can learn. Also as a sidenote if you have a better way as to how I should have worded the question, let me know.
Thanks (:
http://jsfiddle.net/TbCYH/1/
It's good you see the use of making a function of a re-occurring pattern.
Before posting it in StackOverflow, have you tried doing it yourself?
jsfiddle: http://jsfiddle.net/kychan/W7Jxu/
// we will use a container to place our blocks.
// fetch the element by id and store it in a variable.
var container = document.getElementById('container');
function block(mClass, html) {
//extra html you want to store.
return '<div class="' + mClass + '">' + html + '</div>';
}
// code that loops and makes the blocks.
// first part: creates var i
// second: condition, if 'i' is still smaller than three, then loop.
// third part: increment i by 1;
for (var i = 0; i < 3; i++) {
// append the result of function 'block()' to the innerHTML
// of the container.
container.innerHTML += block('block', 'data');
}
Edit: JS has changed a lot since the original post. If you do not require compatibility, use const, template literals, class and querySelector to make the code a bit cleaner. The following code has a Builder class and assumes there is a div with ID 'container':
// create class builder.
class Builder {
// create constructor, accept an element selector, i.e #container.
constructor(targetContainerSelector) {
// search element by given selector and store it as a property.
this.targetContainer = document.querySelector(targetContainerSelector);
}
// method to append to innerHtml of target container.
appendUsingInnerHtml(divAsHtml) {
this.targetContainer.innerHTML += divAsHtml;
}
// method to append to target container using DOM elements.
appendUsingDom(divAsDom) {
this.targetContainer.appendChild(divAsDom);
}
}
// constant to hold element selector.
const myTargetContainer = '#container';
// constant to set the class if required.
const myDivClass = 'my-class';
// constant to hold the instantiated Builder object.
const builder = new Builder(myTargetContainer);
// loop 3 times.
for (let i=0; i<3; i++) {
// call method to append to target container using innerHtml.
builder.appendUsingInnerHtml(`<div class="${myDivClass}}">innerhtml div text</div>`);
// OR.. build using DOM objects.
// create the div element.
const div = document.createElement('div');
// create text element, add some text to it and append it to created div.
div.appendChild(document.createTextNode('dom div text'));
// call method to append div DOM object to target container.
builder.appendUsingDom(div);
}
Please note: Every time something is added to the DOM, it forces the browser to reflow the DOM (computation of element's position and geometry).
Adding everything at once, improve speed, efficiency and performance of a code.
(ref: document.createDocumentFragment)
window.onload = Create();
function Create() {
// create the container
var mainContainer = document.createElement('div');
mainContainer.id = 'mainContainer';
// add all style in one go
mainContainer.setAttribute('style', 'witdht: 400px; height: 200px; border: 2px solid green; margin-left: 20px;');
var divBlocks1 = document.createElement('div');
divBlocks1.className = 'blocks';
divBlocks1.setAttribute('style', 'width: 100px; heigth: 100px; border: 1px solid black; margin-left: 20px; margin-top: 10px; floar: left;');
var divBlocks2 = divBlocks1.cloneNode(false); // copy/clone above div
var divBlocks3 = divBlocks1.cloneNode(false); // copy/clone above div
// everything is still in memory
mainContainer.appendChild(divBlocks1);
mainContainer.appendChild(divBlocks2);
mainContainer.appendChild(divBlocks3);
// now we append everything to the document
document.body.appendChild(mainContainer);
}
Good luck
:)
for(var d=0;d<10;d++){
var aDiv = document.createElement('div');
aDiv.className = "block";
document.getElementsByTagName('body')[0].appendChild(aDiv);
}
Rather than creating the elements before hand and then appending them to the main container, consider dynamically creating and appending them in a loop.
http://jsfiddle.net/TbCYH/6/
for(var i = 0; i < 3; i++) {
var divBlock = document.createElement("div");
divBlock.className = "blocks";
mainContainer.appendChild(divBlock);
}
In the above code snippet a div is being created and appended for each iteration of the loop (which is set to cease at 3).
Also if possible, always use CSS classes rather than modifying the styles for each div directly.
I want to know if we can change tag name in a tag rather than its content. i have this content
< wns id="93" onclick="wish(id)">...< /wns>
in wish function i want to change it to
< lmn id="93" onclick="wish(id)">...< /lmn>
i tried this way
document.getElementById("99").innerHTML =document.getElementById("99").replace(/wns/g,"lmn")
but it doesnot work.
plz note that i just want to alter that specific tag with specific id rather than every wns tag..
Thank you.
You can't change the tag name of an existing DOM element; instead, you have to create a replacement and then insert it where the element was.
The basics of this are to move the child nodes into the replacement and similarly to copy the attributes. So for instance:
var wns = document.getElementById("93");
var lmn = document.createElement("lmn");
var index;
// Copy the children
while (wns.firstChild) {
lmn.appendChild(wns.firstChild); // *Moves* the child
}
// Copy the attributes
for (index = wns.attributes.length - 1; index >= 0; --index) {
lmn.attributes.setNamedItem(wns.attributes[index].cloneNode());
}
// Replace it
wns.parentNode.replaceChild(lmn, wns);
Live Example: (I used div and p rather than wns and lmn, and styled them via a stylesheet with borders so you can see the change)
document.getElementById("theSpan").addEventListener("click", function() {
alert("Span clicked");
}, false);
document.getElementById("theButton").addEventListener("click", function() {
var wns = document.getElementById("target");
var lmn = document.createElement("p");
var index;
// Copy the children
while (wns.firstChild) {
lmn.appendChild(wns.firstChild); // *Moves* the child
}
// Copy the attributes
for (index = wns.attributes.length - 1; index >= 0; --index) {
lmn.attributes.setNamedItem(wns.attributes[index].cloneNode());
}
// Insert it
wns.parentNode.replaceChild(lmn, wns);
}, false);
div {
border: 1px solid green;
}
p {
border: 1px solid blue;
}
<div id="target" foo="bar" onclick="alert('hi there')">
Content before
<span id="theSpan">span in the middle</span>
Content after
</div>
<input type="button" id="theButton" value="Click Me">
See this gist for a reusable function.
Side note: I would avoid using id values that are all digits. Although they're valid in HTML (as of HTML5), they're invalid in CSS and thus you can't style those elements, or use libraries like jQuery that use CSS selectors to interact with them.
var element = document.getElementById("93");
element.outerHTML = element.outerHTML.replace(/wns/g,"lmn");
FIDDLE
There are several problems with your code:
HTML element IDs must start with an alphabetic character.
document.getElementById("99").replace(/wns/g,"lmn") is effectively running a replace command on an element. Replace is a string method so this causes an error.
You're trying to assign this result to document.getElementById("99").innerHTML, which is the HTML inside the element (the tags, attributes and all are part of the outerHTML).
You can't change an element's tagname dynamically, since it fundamentally changes it's nature. Imagine changing a textarea to a select… There are so many attributes that are exclusive to one, illegal in the other: the system cannot work!
What you can do though, is create a new element, and give it all the properties of the old element, then replace it:
<wns id="e93" onclick="wish(id)">
...
</wns>
Using the following script:
// Grab the original element
var original = document.getElementById('e93');
// Create a replacement tag of the desired type
var replacement = document.createElement('lmn');
// Grab all of the original's attributes, and pass them to the replacement
for(var i = 0, l = original.attributes.length; i < l; ++i){
var nodeName = original.attributes.item(i).nodeName;
var nodeValue = original.attributes.item(i).nodeValue;
replacement.setAttribute(nodeName, nodeValue);
}
// Persist contents
replacement.innerHTML = original.innerHTML;
// Switch!
original.parentNode.replaceChild(replacement, original);
Demo here: http://jsfiddle.net/barney/kDjuf/
You can replace the whole tag using jQuery
var element = $('#99');
element.replaceWith($(`<lmn id="${element.attr('id')}">${element.html()}</lmn>`));
[...document.querySelectorAll('.example')].forEach(div => {
div.outerHTML =
div.outerHTML
.replace(/<div/g, '<span')
.replace(/<\/div>/g, '</span>')
})
<div class="example">Hello,</div>
<div class="example">world!</div>
You can achieve this by using JavaScript or jQuery.
We can delete the DOM Element(tag in this case) and recreate using .html or .append menthods in jQuery.
$("#div-name").html("<mytag>Content here</mytag>");
OR
$("<mytag>Content here</mytag>").appendTo("#div-name");
Does someone know how to empty the content of a div (without destroying it) in JavaScript?
Thanks,
Bruno
If your div looks like this:
<div id="MyDiv">content in here</div>
Then this Javascript:
document.getElementById("MyDiv").innerHTML = "";
will make it look like this:
<div id="MyDiv"></div>
If you're using jQuery ...
$('div').html('');
or
$('div').empty();
An alternative way to do it is:
var div = document.getElementById('myDiv');
while(div.firstChild)
div.removeChild(div.firstChild);
However, using document.getElementById('myDiv').innerHTML = ""; is faster.
See: Benchmark test
N.B.
Both methods preserve the div.
If by saying without destroying it, you mean to a keep a reference to the children, you can do:
var oldChildren = [];
while(element.hasChildNodes()) {
oldChildren.push(element.removeChild(element.firstChild));
}
Regarding the original tagging (html css) of your question:
You cannot remove content with CSS. You could only hide it. E.g. you can hide all children of a certain node with:
#someID > * {
display: none;
}
This doesn't work in IE6 though (but you could use #someID *).
In jQuery it would be as simple as $('#yourDivID').empty()
See the documentation.
This method works best to me:
Element.prototype.remove = function() {
this.parentElement.removeChild(this);
}
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
for(var i = this.length - 1; i >= 0; i--) {
if(this[i] && this[i].parentElement) {
this[i].parentElement.removeChild(this[i]);
}
}
}
To use it we can deploy like this:
document.getElementsByID('DIV_Id').remove();
or
document.getElementsByClassName('DIV_Class').remove();
you can .remove() each child:
const div = document.querySelector('div.my-div')
while(div.firstChild) div.firstChild.remove()
You can empty your DOM using this:
const el = document.getElementById("MyDiv");
while (el.firstChild) {
el.removeChild(el.firstChild);
}
This is supposed to be faster than the traditionally used method : document.getElementById("MyDiv").innerHTML = "";
How can I implement prepend and append with regular JavaScript without using jQuery?
Here's a snippet to get you going:
theParent = document.getElementById("theParent");
theKid = document.createElement("div");
theKid.innerHTML = 'Are we there yet?';
// append theKid to the end of theParent
theParent.appendChild(theKid);
// prepend theKid to the beginning of theParent
theParent.insertBefore(theKid, theParent.firstChild);
theParent.firstChild will give us a reference to the first element within theParent and put theKid before it.
Perhaps you're asking about the DOM methods appendChild and insertBefore.
parentNode.insertBefore(newChild, refChild)
Inserts the node newChild as a child of parentNode before the
existing child node refChild. (Returns newChild.)
If refChild is null, newChild is added at the end of the list of
children. Equivalently, and more readably, use
parentNode.appendChild(newChild).
You didn't give us much to go on here, but I think you're just asking how to add content to the beginning or end of an element?
If so here's how you can do it pretty easily:
//get the target div you want to append/prepend to
var someDiv = document.getElementById("targetDiv");
//append text
someDiv.innerHTML += "Add this text to the end";
//prepend text
someDiv.innerHTML = "Add this text to the beginning" + someDiv.innerHTML;
Pretty easy.
If you want to insert a raw HTML string no matter how complex, you can use:
insertAdjacentHTML, with appropriate first argument:
'beforebegin'
Before the element itself.
'afterbegin'
Just inside the element, before its first child.
'beforeend'
Just inside the element, after its last child.
'afterend'
After the element itself.
Hint: you can always call Element.outerHTML to get the HTML string representing the element to be inserted.
An example of usage:
document.getElementById("foo").insertAdjacentHTML("beforeBegin",
"<div><h1>I</h1><h2>was</h2><h3>inserted</h3></div>");
DEMO
Caution: insertAdjacentHTML does not preserve listeners that where attached with .addEventLisntener.
I added this on my project and it seems to work:
HTMLElement.prototype.prependHtml = function (element) {
const div = document.createElement('div');
div.innerHTML = element;
this.insertBefore(div, this.firstChild);
};
HTMLElement.prototype.appendHtml = function (element) {
const div = document.createElement('div');
div.innerHTML = element;
while (div.children.length > 0) {
this.appendChild(div.children[0]);
}
};
Example:
document.body.prependHtml(`Hello World`);
document.body.appendHtml(`Hello World`);
Here's an example of using prepend to add a paragraph to the document.
var element = document.createElement("p");
var text = document.createTextNode("Example text");
element.appendChild(text);
document.body.prepend(element);
result:
<p>Example text</p>
In order to simplify your life you can extend the HTMLElement object. It might not work for older browsers, but definitely makes your life easier:
HTMLElement = typeof(HTMLElement) != 'undefined' ? HTMLElement : Element;
HTMLElement.prototype.prepend = function(element) {
if (this.firstChild) {
return this.insertBefore(element, this.firstChild);
} else {
return this.appendChild(element);
}
};
So next time you can do this:
document.getElementById('container').prepend(document.getElementById('block'));
// or
var element = document.getElementById('anotherElement');
document.body.prepend(div);
In 2017 I know for Edge 15 and IE 12, the prepend method isn't included as a property for Div elements, but if anyone needs a quick reference to polyfill a function I made this:
HTMLDivElement.prototype.prepend = (node, ele)=>{
try { node.insertBefore(ele ,node.children[0]);}
catch (e){ throw new Error(e.toString()) } }
Simple arrow function that's compatible with most modern browsers.
var insertedElement = parentElement.insertBefore(newElement, referenceElement);
If referenceElement is null, or undefined, newElement is inserted at the end of the list of child nodes.
insertedElement The node being inserted, that is newElement
parentElement The parent of the newly inserted node.
newElement The node to insert.
referenceElement The node before which newElement is inserted.
Examples can be found here: Node.insertBefore
You can also use unshift() to prepend to a list
document.write() is not a good practice, some browsers like Chrome give you a warning if you use it, and it may be a bad solution if you are providing it to a customer, they don't want to use your code and see warnings in the debug console!
Also jQuery may also be a bad thing if you are giving your code to a customer who already uses jQuery for other functionality on their site, there will be a conflict if there is already a different version of jQuery running.
If you want to insert content into an iframe, and do that with pure JS, and with no JQuery, and without document.write(), I have a solution.
You can use the following steps
1.Select your iframe:
var iframe = document.getElementById("adblock_iframe");
2.Create an element that you want to insert into the frame, let's say an image:
var img = document.createElement('img');
img.src = "https://server-name.com/upload/adblock" + id + ".jpg";
img.style.paddingLeft = "450px";
//scale down the image is we have a high resolution screen on the client side
if (retina_test_media == true && high_res_test == true) {
img.style.width = "200px";
img.style.height = "50px";
} else {
img.style.width = "400px";
img.style.height = "100px";
}
img.id = "image";
3.Insert the image element into the iframe:
iframe.contentWindow.document.body.appendChild(img);
This is not best way to do it but if anyone wants to insert an element before everything, here is a way.
var newElement = document.createElement("div");
var element = document.getElementById("targetelement");
element.innerHTML = '<div style="display:none !important;"></div>' + element.innerHTML;
var referanceElement = element.children[0];
element.insertBefore(newElement,referanceElement);
element.removeChild(referanceElement);