I want to wrap all the nodes within the #slidesContainer div with JavaScript. I know it is easily done in jQuery, but I am interested in knowing how to do it with pure JS.
Here is the code:
<div id="slidesContainer">
<div class="slide">slide 1</div>
<div class="slide">slide 2</div>
<div class="slide">slide 3</div>
<div class="slide">slide 4</div>
</div>
I want to wrap the divs with a class of "slide" collectively within another div with id="slideInner".
If your "slide"s are always in slidesContainer you could do this
org_html = document.getElementById("slidesContainer").innerHTML;
new_html = "<div id='slidesInner'>" + org_html + "</div>";
document.getElementById("slidesContainer").innerHTML = new_html;
Like BosWorth99, I also like to manipulate the dom elements directly, this helps maintain all of the node's attributes. However, I wanted to maintain the position of the element in the dom and not just append the end incase there were siblings. Here is what I did.
var wrap = function (toWrap, wrapper) {
wrapper = wrapper || document.createElement('div');
toWrap.parentNode.appendChild(wrapper);
return wrapper.appendChild(toWrap);
};
How to "wrap content" and "preserve bound events"?
// element that will be wrapped
var el = document.querySelector('div.wrap_me');
// create wrapper container
var wrapper = document.createElement('div');
// insert wrapper before el in the DOM tree
el.parentNode.insertBefore(wrapper, el);
// move el into wrapper
wrapper.appendChild(el);
or
function wrap(el, wrapper) {
el.parentNode.insertBefore(wrapper, el);
wrapper.appendChild(el);
}
// example: wrapping an anchor with class "wrap_me" into a new div element
wrap(document.querySelector('div.wrap_me'), document.createElement('div'));
ref
https://plainjs.com/javascript/manipulation/wrap-an-html-structure-around-an-element-28
If you patch up document.getElementsByClassName for IE, you can do something like:
var addedToDocument = false;
var wrapper = document.createElement("div");
wrapper.id = "slideInner";
var nodesToWrap = document.getElementsByClassName("slide");
for (var index = 0; index < nodesToWrap.length; index++) {
var node = nodesToWrap[index];
if (! addedToDocument) {
node.parentNode.insertBefore(wrapper, node);
addedToDocument = true;
}
node.parentNode.removeChild(node);
wrapper.appendChild(node);
}
Example: http://jsfiddle.net/GkEVm/2/
A general good tip for trying to do something you'd normally do with jQuery, without jQuery, is to look at the jQuery source. What do they do? Well, they grab all the children, append them to a a new node, then append that node inside the parent.
Here's a simple little method to do precisely that:
const wrapAll = (target, wrapper = document.createElement('div')) => {
;[ ...target.childNodes ].forEach(child => wrapper.appendChild(child))
target.appendChild(wrapper)
return wrapper
}
And here's how you use it:
// wraps everything in a div named 'wrapper'
const wrapper = wrapAll(document.body)
// wraps all the children of #some-list in a new ul tag
const newList = wrapAll(document.getElementById('some-list'), document.createElement('ul'))
I like to manipulate dom elements directly - createElement, appendChild, removeChild etc. as opposed to the injection of strings as element.innerHTML. That strategy does work, but I think the native browser methods are more direct. Additionally, they returns a new node's value, saving you from another unnecessary getElementById call.
This is really simple, and would need to be attached to some type of event to make any use of.
wrap();
function wrap() {
var newDiv = document.createElement('div');
newDiv.setAttribute("id", "slideInner");
document.getElementById('wrapper').appendChild(newDiv);
newDiv.appendChild(document.getElementById('slides'));
}
jsFiddle
Maybe that helps your understanding of this issue with vanilla js.
To simply wrap a div without the need of the parent:
<div id="original">ORIGINAL</div>
<script>
document.getElementById('original').outerHTML
=
'<div id="wrap">'+
document.getElementById('original').outerHTML
+'</div>'
</script>
Working Example: https://jsfiddle.net/0v5eLo29/
More Practical Way:
const origEle = document.getElementById('original');
origEle.outerHTML = '<div id="wrap">' + origEle.outerHTML + '</div>';
Or by using only nodes:
let original = document.getElementById('original');
let wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
wrapper.append(original.cloneNode(true));
original.replaceWith(wrapper);
Working Example: https://jsfiddle.net/wfhqak2t/
A simple way to do this would be:
let el = document.getElementById('slidesContainer');
el.innerHTML = `<div id='slideInner'>${el.innerHTML}</div>`;
Note - below answers the title of the question but is not specific to the OP's requirements (which are over a decade old)
Using the range API is making wrapping easy, by creating a Range which selects only the node wished to be wrapped, and then use the surroundContents API to wrap it.
Below code wraps the first (text) node with a <mark> element and the last node with a <u> element:
const wrapNode = (nodeToWrap, wrapWith) => {
const range = document.createRange();
range.selectNode(nodeToWrap);
range.surroundContents(wrapWith);
}
wrapNode(document.querySelector('p').firstChild, document.createElement('mark'))
wrapNode(document.querySelector('p').lastChild, document.createElement('u'))
<p>
first node
<span>second node</span>
third node
</p>
From what I understand #Michal 's answer is vulnerable to XXS attacks (using innerHTML is a security vulnerability) Here is another link on this.
There are many ways to do this, one that I found and liked is:
function wrap_single(el, wrapper) {
el.parentNode.insertBefore(wrapper, el);
wrapper.appendChild(el);
}
let divWrapper;
let elementToWrap;
elementToWrap = document.querySelector('selector');
// wrapping the event form in a row
divWrapper = document.createElement('div');
divWrapper.className = 'row';
wrap_single(elementToWrap, divWrapper);
This works well. However for me, I sometimes want to just wrap parts of an element. So I modified the function to this:
function wrap_some_children(el, wrapper, counter) {
el.parentNode.insertBefore(wrapper, el);
if ( ! counter ) {
counter = el.childNodes.length;
}
for(i = 0; i < counter; i++) {
wrapper.appendChild( el.childNodes[0] );
}
}
// wrapping parts of the event form into columns
let divCol1;
let divCol2;
// the elements to wrap
elementToWrap = document.querySelector('selector');
// creating elements to wrap with
divCol1 = document.createElement('div');
divCol1.className = 'col-sm-6';
divCol2 = document.createElement('div');
divCol2.className = 'col-sm-6';
// for the first column
wrap_some_children(elementToWrap, divCol1, 13); // only wraps the first 13 child nodes
// for the second column
wrap_some_children(elementToWrap, divCol2);
I hope this helps.
wrapInner multiple tag content
function wilWrapInner(el, wrapInner) {
var _el = [].slice.call(el.children);
var fragment = document.createDocumentFragment();
el.insertAdjacentHTML('afterbegin', wrapInner);
var _wrap = el.children[0];
for (var i = 0, len = _el.length; i < len; i++) {
fragment.appendChild(_el[i]);
}
_wrap.appendChild(fragment);
}
Link Demo Jsbin
Related
I am looking to replace an element in the DOM.
For example, there is an <a> element that I want to replace with a <span> instead.
How would I go and do that?
by using replaceChild():
<html>
<head>
</head>
<body>
<div>
<a id="myAnchor" href="http://www.stackoverflow.com">StackOverflow</a>
</div>
<script type="text/JavaScript">
var myAnchor = document.getElementById("myAnchor");
var mySpan = document.createElement("span");
mySpan.innerHTML = "replaced anchor!";
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
</script>
</body>
</html>
A.replaceWith(span) - No parent needed
Generic form:
target.replaceWith(element)
Way better/cleaner than the previous method.
For your use case:
A.replaceWith(span)
Advanced usage
You can pass multiple values (or use spread operator ...).
Any string value will be added as a text element.
Examples:
// Initially [child1, target, child3]
target.replaceWith(span, "foo") // [child1, span, "foo", child3]
const list = ["bar", span]
target.replaceWith(...list, "fizz") // [child1, "bar", span, "fizz", child3]
Safely handling null target
If your target has a chance to be null, you can consider using the newish ?. optional chaining operator. Nothing will happen if target doesn't exist. Read more here.
target?.replaceWith?.(element)
Related DOM methods
Read More - child.before and child.after
Read More - parent.prepend and parent.append
Mozilla Docs
Supported Browsers - 97% Nov '22
var a = A.parentNode.replaceChild(document.createElement("span"), A);
a is the replaced A element.
This question is very old, but I found myself studying for a Microsoft Certification, and in the study book it was suggested to use:
oldElement.replaceNode(newElement)
I looked it up and it seems to only be supported in IE. Doh..
I thought I'd just add it here as a funny side note ;)
I had a similar issue and found this thread. Replace didn't work for me, and going by the parent was difficult for my situation. Inner Html replaced the children, which wasn't what I wanted either. Using outerHTML got the job done. Hope this helps someone else!
currEl = <div>hello</div>
newElem = <span>Goodbye</span>
currEl.outerHTML = newElem
# currEl = <span>Goodbye</span>
You can replace an HTML Element or Node using Node.replaceWith(newNode).
This example should keep all attributes and childs from origin node:
const links = document.querySelectorAll('a')
links.forEach(link => {
const replacement = document.createElement('span')
// copy attributes
for (let i = 0; i < link.attributes.length; i++) {
const attr = link.attributes[i]
replacement.setAttribute(attr.name, attr.value)
}
// copy content
replacement.innerHTML = link.innerHTML
// or you can use appendChild instead
// link.childNodes.forEach(node => replacement.appendChild(node))
link.replaceWith(replacement)
})
If you have these elements:
Link 1
Link 2
Link 3
Link 4
After running above codes, you will end up with these elements:
<span href="#link-1">Link 1</span>
<span href="#link-2">Link 2</span>
<span href="#link-3">Link 3</span>
<span href="#link-4">Link 4</span>
You can use replaceChild on the parent of the target element after creating your new element (createElement):
const newElement = document.createElement(/*...*/);
const target = document.getElementById("my-table");
target.parentNode.replaceChild(newElement, target);
If your starting point for the new element is HTML, you can use insertAdjacentHTML and then removeChild on the parent (or remove on the element itself, in modern environments):
const target = document.getElementById("my-table");
target.insertAdjacentHTML("afterend", theHTMLForTheNewElement);
target.parentNode.removeChild(target); // Or: `target.remove()`
Best way to do it. No parents need. Just use Element.outerHTML = template;
// Get the current element
var currentNode = document.querySelector('#greeting');
// Replace the element
currentNode.outerHTML =
'<div id="salutations">' +
'<h1>Hi, universe!</h1>' +
'<p>The sun is always shining!</p>' +
'</div>';
Example for replacing LI elements
function (element) {
let li = element.parentElement;
let ul = li.parentNode;
if (li.nextSibling.nodeName === 'LI') {
let li_replaced = ul.replaceChild(li, li.nextSibling);
ul.insertBefore(li_replaced, li);
}
}
Given the already proposed options the easiest solution without finding a parent:
var parent = document.createElement("div");
var child = parent.appendChild(document.createElement("a"));
var span = document.createElement("span");
// for IE
if("replaceNode" in child)
child.replaceNode(span);
// for other browsers
if("replaceWith" in child)
child.replaceWith(span);
console.log(parent.outerHTML);
I want to wrap all the nodes within the #slidesContainer div with JavaScript. I know it is easily done in jQuery, but I am interested in knowing how to do it with pure JS.
Here is the code:
<div id="slidesContainer">
<div class="slide">slide 1</div>
<div class="slide">slide 2</div>
<div class="slide">slide 3</div>
<div class="slide">slide 4</div>
</div>
I want to wrap the divs with a class of "slide" collectively within another div with id="slideInner".
If your "slide"s are always in slidesContainer you could do this
org_html = document.getElementById("slidesContainer").innerHTML;
new_html = "<div id='slidesInner'>" + org_html + "</div>";
document.getElementById("slidesContainer").innerHTML = new_html;
Like BosWorth99, I also like to manipulate the dom elements directly, this helps maintain all of the node's attributes. However, I wanted to maintain the position of the element in the dom and not just append the end incase there were siblings. Here is what I did.
var wrap = function (toWrap, wrapper) {
wrapper = wrapper || document.createElement('div');
toWrap.parentNode.appendChild(wrapper);
return wrapper.appendChild(toWrap);
};
How to "wrap content" and "preserve bound events"?
// element that will be wrapped
var el = document.querySelector('div.wrap_me');
// create wrapper container
var wrapper = document.createElement('div');
// insert wrapper before el in the DOM tree
el.parentNode.insertBefore(wrapper, el);
// move el into wrapper
wrapper.appendChild(el);
or
function wrap(el, wrapper) {
el.parentNode.insertBefore(wrapper, el);
wrapper.appendChild(el);
}
// example: wrapping an anchor with class "wrap_me" into a new div element
wrap(document.querySelector('div.wrap_me'), document.createElement('div'));
ref
https://plainjs.com/javascript/manipulation/wrap-an-html-structure-around-an-element-28
If you patch up document.getElementsByClassName for IE, you can do something like:
var addedToDocument = false;
var wrapper = document.createElement("div");
wrapper.id = "slideInner";
var nodesToWrap = document.getElementsByClassName("slide");
for (var index = 0; index < nodesToWrap.length; index++) {
var node = nodesToWrap[index];
if (! addedToDocument) {
node.parentNode.insertBefore(wrapper, node);
addedToDocument = true;
}
node.parentNode.removeChild(node);
wrapper.appendChild(node);
}
Example: http://jsfiddle.net/GkEVm/2/
A general good tip for trying to do something you'd normally do with jQuery, without jQuery, is to look at the jQuery source. What do they do? Well, they grab all the children, append them to a a new node, then append that node inside the parent.
Here's a simple little method to do precisely that:
const wrapAll = (target, wrapper = document.createElement('div')) => {
;[ ...target.childNodes ].forEach(child => wrapper.appendChild(child))
target.appendChild(wrapper)
return wrapper
}
And here's how you use it:
// wraps everything in a div named 'wrapper'
const wrapper = wrapAll(document.body)
// wraps all the children of #some-list in a new ul tag
const newList = wrapAll(document.getElementById('some-list'), document.createElement('ul'))
I like to manipulate dom elements directly - createElement, appendChild, removeChild etc. as opposed to the injection of strings as element.innerHTML. That strategy does work, but I think the native browser methods are more direct. Additionally, they returns a new node's value, saving you from another unnecessary getElementById call.
This is really simple, and would need to be attached to some type of event to make any use of.
wrap();
function wrap() {
var newDiv = document.createElement('div');
newDiv.setAttribute("id", "slideInner");
document.getElementById('wrapper').appendChild(newDiv);
newDiv.appendChild(document.getElementById('slides'));
}
jsFiddle
Maybe that helps your understanding of this issue with vanilla js.
To simply wrap a div without the need of the parent:
<div id="original">ORIGINAL</div>
<script>
document.getElementById('original').outerHTML
=
'<div id="wrap">'+
document.getElementById('original').outerHTML
+'</div>'
</script>
Working Example: https://jsfiddle.net/0v5eLo29/
More Practical Way:
const origEle = document.getElementById('original');
origEle.outerHTML = '<div id="wrap">' + origEle.outerHTML + '</div>';
Or by using only nodes:
let original = document.getElementById('original');
let wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
wrapper.append(original.cloneNode(true));
original.replaceWith(wrapper);
Working Example: https://jsfiddle.net/wfhqak2t/
A simple way to do this would be:
let el = document.getElementById('slidesContainer');
el.innerHTML = `<div id='slideInner'>${el.innerHTML}</div>`;
Note - below answers the title of the question but is not specific to the OP's requirements (which are over a decade old)
Using the range API is making wrapping easy, by creating a Range which selects only the node wished to be wrapped, and then use the surroundContents API to wrap it.
Below code wraps the first (text) node with a <mark> element and the last node with a <u> element:
const wrapNode = (nodeToWrap, wrapWith) => {
const range = document.createRange();
range.selectNode(nodeToWrap);
range.surroundContents(wrapWith);
}
wrapNode(document.querySelector('p').firstChild, document.createElement('mark'))
wrapNode(document.querySelector('p').lastChild, document.createElement('u'))
<p>
first node
<span>second node</span>
third node
</p>
From what I understand #Michal 's answer is vulnerable to XXS attacks (using innerHTML is a security vulnerability) Here is another link on this.
There are many ways to do this, one that I found and liked is:
function wrap_single(el, wrapper) {
el.parentNode.insertBefore(wrapper, el);
wrapper.appendChild(el);
}
let divWrapper;
let elementToWrap;
elementToWrap = document.querySelector('selector');
// wrapping the event form in a row
divWrapper = document.createElement('div');
divWrapper.className = 'row';
wrap_single(elementToWrap, divWrapper);
This works well. However for me, I sometimes want to just wrap parts of an element. So I modified the function to this:
function wrap_some_children(el, wrapper, counter) {
el.parentNode.insertBefore(wrapper, el);
if ( ! counter ) {
counter = el.childNodes.length;
}
for(i = 0; i < counter; i++) {
wrapper.appendChild( el.childNodes[0] );
}
}
// wrapping parts of the event form into columns
let divCol1;
let divCol2;
// the elements to wrap
elementToWrap = document.querySelector('selector');
// creating elements to wrap with
divCol1 = document.createElement('div');
divCol1.className = 'col-sm-6';
divCol2 = document.createElement('div');
divCol2.className = 'col-sm-6';
// for the first column
wrap_some_children(elementToWrap, divCol1, 13); // only wraps the first 13 child nodes
// for the second column
wrap_some_children(elementToWrap, divCol2);
I hope this helps.
wrapInner multiple tag content
function wilWrapInner(el, wrapInner) {
var _el = [].slice.call(el.children);
var fragment = document.createDocumentFragment();
el.insertAdjacentHTML('afterbegin', wrapInner);
var _wrap = el.children[0];
for (var i = 0, len = _el.length; i < len; i++) {
fragment.appendChild(_el[i]);
}
_wrap.appendChild(fragment);
}
Link Demo Jsbin
I have the following jQuery code to inject an element after another element and then MOVE some elements into it. I wasn't able to find any clear vanilla js functions do this as most people were suggesting insertBefore or insertAdjacentElement but none of them work in the chrome console. I'm trying to just do this in vanilla js.
jQuery('<div id="jsinjected"><i class="field-name-title"></i></div>').insertAfter('.field-name-body');
jQuery('.group-header .field-name-title ~ .field').appendTo('#jsinjected');
Will this work?
const after = document.getElementById("element");
const move = document.getElementById("move");
const parent = after.parentNode;
const tmp = document.createElement("div");
tmp.innerHTML =
`<div id="jsinjected"><i class="field-name-title">injected</i></div>`;
const newElement = tmp.firstElementChild;
var next = after.nextSibling;
if(next === null){
next = document.createTextNode(" ");
parent.appendChild(next);
}
parent.insertBefore(newElement, next);
newElement.appendChild(move);
<div><div id="move">move this</div>
<div id="element">after</div></div>
Any ideas on how I would convert this jQuery to vanilla JS:
$('.section > h1').after('<p>This paragraph was inserted with jQuery</p>');
I am new to jQuery and even newer to vanilla JS.
This is as far as I got:
var newP = document.createElement('p');
var pTxt = document.createTextNode('This paragraph was inserted with JavaScript');
var header = document.getElementsByTagName('h1');
Not sure where to go from here?
jQuery does a lot for you behind the scenes. The equivalent plain DOM code might look something like this:
// Get all header elements
var header = document.getElementsByTagName('h1'),
parent,
newP,
text;
// Loop through the elements
for (var i=0, m = header.length; i < m; i++) {
parent = header[i].parentNode;
// Check for "section" in the parent's classname
if (/(?:^|\s)section(?:\s|$)/i.test(parent.className)) {
newP = document.createElement("p");
text = document.createTextNode('This paragraph was inserted with JavaScript');
newP.appendChild(text);
// Insert the new P element after the header element in its parent node
parent.insertBefore(newP, header[i].nextSibling);
}
}
See it in action
Note that you can also use textContent/innerText instead of creating the text node. It's good that you're trying to learn how to directly manipulate the DOM rather than just letting jQuery do all the work. It's nice to understand this stuff, just remember that jQuery and other frameworks are there to lighten these loads for you :)
You might find this function useful (I didn't test)
function insertAfter(node, referenceNode) {
referenceNode.parentNode.insertBefore(node, referenceNode.nextSibling);
}
Oh it's not so bad...
var h1s = document.getElementsByTagName('h1');
for (var i=0, l=h1s.length; i<l; i++) {
var h1 = h1s[i], parent = h1.parentNode;
if (parent.className.match(/\bsection\b/i)) {
var p = document.createElement('p');
p.innerHTML = 'This paragraph was inserted with JavaScript';
parent.insertBefore(p, h1.nextSibling);
}
}
Imagine I have the following HTML:
<div><span><b>This is in bold</b></span></div>
I want to get the HTML for the div, including the div itself. Element.innerHTML only returns:
<span>...</span>
Any ideas? Thanks
Use outerHTML:
var el = document.getElementById( 'foo' );
alert( el.outerHTML );
Expanding on jldupont's answer, you could create a wrapping element on the fly:
var target = document.getElementById('myElement');
var wrap = document.createElement('div');
wrap.appendChild(target.cloneNode(true));
alert(wrap.innerHTML);
I am cloning the element to avoid having to remove and reinsert the element in the actual document. This might be expensive if the element you wish to print has a very large tree below it, though.
First, put on element that wraps the div in question, put an id attribute on the element and then use getElementById on it: once you've got the lement, just do 'e.innerHTML` to retrieve the HTML.
<div><span><b>This is in bold</b></span></div>
=>
<div id="wrap"><div><span><b>This is in bold</b></span></div></div>
and then:
var e=document.getElementById("wrap");
var content=e.innerHTML;
Note that outerHTML is not cross-browser compatible.
old question but for newcomers that come around :
document.querySelector('div').outerHTML
You'll want something like this for it to be cross browser.
function OuterHTML(element) {
var container = document.createElement("div");
container.appendChild(element.cloneNode(true));
return container.innerHTML;
}
If you want a lighter footprint, but a longer script, get the elements innerHTML and only create and clone the empty parent-
function getHTML(who,lines){
if(!who || !who.tagName) return '';
var txt, ax, str, el= document.createElement('div');
el.appendChild(who.cloneNode(false));
txt= el.innerHTML;
ax= txt.indexOf('>')+1;
str= txt.substring(0, ax)+who.innerHTML+ txt.substring(ax);
el= null;
return lines? str.replace(/> *</g,'>\n<'): str;
//easier to read if elements are separated
}
var x = $('#container').get(0).outerHTML;
as outerHTML is IE only, use this function:
function getOuterHtml(node) {
var parent = node.parentNode;
var element = document.createElement(parent.tagName);
element.appendChild(node);
var html = element.innerHTML;
parent.appendChild(node);
return html;
}
creates a bogus empty element of the type parent and uses innerHTML on it and then reattaches the element back into the normal dom
define function outerHTML based on support for element.outerHTML:
var temp_container = document.createElement("div"); // empty div not added to DOM
if (temp_container.outerHTML){
var outerHTML = function(el){return el.outerHTML||el.nodeValue} // e.g. textnodes do not have outerHTML
} else { // when .outerHTML is not supported
var outerHTML = function(el){
var clone = el.cloneNode(true);
temp_container.appendChild(clone);
outerhtml = temp_container.innerHTML;
temp_container.removeChild(clone);
return outerhtml;
};
};
var el = document.getElementById('foo');
el.parentNode.innerHTML;