Wrapping a set of DOM elements using JavaScript - javascript

I have a series of p tags on my page and I want to wrap them all into a container, e.g.
<p>foo</p>
<p>bar</p>
<p>baz</p>
I want to wrap all the above tags into a container as follows:
<div>
<p>foo</p>
<p>bar</p>
<p>baz</p>
</div>
How to wrap a NodeList in an element using vanilla JavaScript?

Posted below are a pure JavaScript version of jQuery's wrap and wrapAll methods. I can't guarantee they work exactly as they do in jQuery, but they do in fact work very similarly and should be able to accomplish the same tasks. They work with either a single HTMLElement or an array of them. I haven't tested to confirm, but they should both work in all modern browsers (and older ones to a certain extent).
Unlike the selected answer, these methods maintain the correct HTML structure by using insertBefore as well as appendChild.
wrap:
// Wrap an HTMLElement around each element in an HTMLElement array.
HTMLElement.prototype.wrap = function(elms) {
// Convert `elms` to an array, if necessary.
if (!elms.length) elms = [elms];
// Loops backwards to prevent having to clone the wrapper on the
// first element (see `child` below).
for (var i = elms.length - 1; i >= 0; i--) {
var child = (i > 0) ? this.cloneNode(true) : this;
var el = elms[i];
// Cache the current parent and sibling.
var parent = el.parentNode;
var sibling = el.nextSibling;
// Wrap the element (is automatically removed from its current
// parent).
child.appendChild(el);
// If the element had a sibling, insert the wrapper before
// the sibling to maintain the HTML structure; otherwise, just
// append it to the parent.
if (sibling) {
parent.insertBefore(child, sibling);
} else {
parent.appendChild(child);
}
}
};
See a working demo on jsFiddle.
wrapAll:
// Wrap an HTMLElement around another HTMLElement or an array of them.
HTMLElement.prototype.wrapAll = function(elms) {
var el = elms.length ? elms[0] : elms;
// Cache the current parent and sibling of the first element.
var parent = el.parentNode;
var sibling = el.nextSibling;
// Wrap the first element (is automatically removed from its
// current parent).
this.appendChild(el);
// Wrap all other elements (if applicable). Each element is
// automatically removed from its current parent and from the elms
// array.
while (elms.length) {
this.appendChild(elms[0]);
}
// If the first element had a sibling, insert the wrapper before the
// sibling to maintain the HTML structure; otherwise, just append it
// to the parent.
if (sibling) {
parent.insertBefore(this, sibling);
} else {
parent.appendChild(this);
}
};
See a working demo on jsFiddle.

You can do like this:
// create the container div
var dv = document.createElement('div');
// get all divs
var divs = document.getElementsByTagName('div');
// get the body element
var body = document.getElementsByTagName('body')[0];
// apply class to container div
dv.setAttribute('class', 'container');
// find out all those divs having class C
for(var i = 0; i < divs.length; i++)
{
if (divs[i].getAttribute('class') === 'C')
{
// put the divs having class C inside container div
dv.appendChild(divs[i]);
}
}
// finally append the container div to body
body.appendChild(dv);

I arrived at this wrapAll function by starting with Kevin's answer and fixing the problems presented below as well as those mentioned in the comments below his answer.
His function attempts to append the wrapper to the next sibling of the first node in the passed nodeList. That will be problematic if that node is also in the nodeList. To see this in action, remove all the text and other elements from between the first and second <li> in his wrapAll demo.
Contrary to the claim, his function won't work if multiple nodes are passed in an array rather than a nodeList because of the looping technique used.
These are fixed below:
// Wrap wrapper around nodes
// Just pass a collection of nodes, and a wrapper element
function wrapAll(nodes, wrapper) {
// Cache the current parent and previous sibling of the first node.
var parent = nodes[0].parentNode;
var previousSibling = nodes[0].previousSibling;
// Place each node in wrapper.
// - If nodes is an array, we must increment the index we grab from
// after each loop.
// - If nodes is a NodeList, each node is automatically removed from
// the NodeList when it is removed from its parent with appendChild.
for (var i = 0; nodes.length - i; wrapper.firstChild === nodes[0] && i++) {
wrapper.appendChild(nodes[i]);
}
// Place the wrapper just after the cached previousSibling,
// or if that is null, just before the first child.
var nextSibling = previousSibling ? previousSibling.nextSibling : parent.firstChild;
parent.insertBefore(wrapper, nextSibling);
return wrapper;
}
See the Demo and GitHub Gist.

Here's my javascript version of wrap(). Shorter but you have to create the element before calling the function.
HTMLElement.prototype.wrap = function(wrapper){
this.parentNode.insertBefore(wrapper, this);
wrapper.appendChild(this);
}
function wrapDiv(){
var wrapper = document.createElement('div'); // create the wrapper
wrapper.style.background = "#0cf"; // add style if you want
var element = document.getElementById('elementID'); // get element to wrap
element.wrap(wrapper);
}
div {
border: 2px solid #f00;
margin-bottom: 10px;
}
<ul id="elementID">
<li>Chair</li>
<li>Sofa</li>
</ul>
<button onclick="wrapDiv()">Wrap the list</button>

If you're target browsers support it, the document.querySelectorAll uses CSS selectors:
var targets = document.querySelectorAll('.c'),
head = document.querySelectorAll('body')[0],
cont = document.createElement('div');
cont.className = "container";
for (var x=0, y=targets.length; x<y; x++){
con.appendChild(targets[x]);
}
head.appendChild(cont);

Taking #Rixius 's answer a step further, you could turn it into a forEach loop with an arrow function
let parent = document.querySelector('div');
let children = parent.querySelectorAll('*');
let wrapper = document.createElement('section');
wrapper.className = "wrapper";
children.forEach((child) => {
wrapper.appendChild(child);
});
parent.appendChild(wrapper);
* { margin: 0; padding: 0; box-sizing: border-box; font-family: roboto; }
body { padding: 5vw; }
span,i,b { display: block; }
div { border: 1px solid lime; margin: 1rem; }
section { border: 1px solid red; margin: 1rem; }
<div>
<span>span</span>
<i>italic</i>
<b>bold</b>
</div>

Related

Only the last element I added using innerHTML keeps its event handlers, why?

I am trying to make a script that injects interactable object information in a list of the markup page. Whenever I try to add an onclick event on a div, it works fine, however whenever I try to add more within a for loop, it does not work the way I intended.
I took a look of what is going on using breakpoints in the webpage debugger, and I see that the problem is that it seems to delete the event on the previous div before adding to the next div. In the end, the only event remaining is the last div after the loop exits.
I want to keep these events on all my divs, not just the last one... what seems to be the problem here?
var objects = ['Tom', 'Sauna', 'Traum'];
for (var i = 0; i < objects.length; i++){
document.getElementById('list').innerHTML += "<div class='item' id='"+ i +"'>" + objects[i] + "</div>";
document.getElementById(i).addEventListener("mouseup", function() {
Select(this);
});
}
function Select(char) {
console.log(char);
}
div.item {
border: 1px solid black;
padding: 4px;
margin: 4px;
}
<div id="list"></div>
When you change innerHTML browser reconstructs the element's contents, throwing away all event handlers attached. Use DOM methods instead:
for (let i = 0; i < objects.length; i++){
var block = document.createElement('div');
block.setAttribute('id', i);
document.getElementById('list').appendChild( block );
block.addEventListener("mouseup", function() {
Select(this);
});
}
UPD: alternatively use a insertAdjacentHTML method instead of redefining innerHTML:
document.getElementById('list').insertAdjacentHTML(
'beforeend', "<div id='"+ i +"'>" + i + "</div>");
The reason is the way you are appending. innerHtml += effectively overwrites the existing content in the list. So, any elements that you added and bound are simply gone, and new items are added each time.
There are a couple ways to make this work.
First instead of assigning an innerHtml you can append elements.
const items = ['taco', 'apple', 'pork'];
const list = document.getElementById("list");
for (const item of items) {
const el = document.createElement("div");
el.addEventListener('click', (e) => console.log(`clicked ${item}`));
el.innerText = item;
list.appendChild(el);
}
<div id="list"></div>
Since we are appending an explicit element and not overwriting content, this will work.
A better approach would be to use delegation. We assign a single event handler onto the list and listen for any clicks. We then figure out what specific element was clicked.
const items = ['taco', 'apple', 'pork'];
const list = document.getElementById("list");
const add = document.getElementById("add");
list.addEventListener('click', (e) => {
const parent = e.target.closest("[data-item]");
if (parent != null) {
console.log(`clicked on ${parent.dataset['item']}`);
}
});
for (const item of items) {
list.innerHTML += `<div data-item="${item}">${item}</div>`;
}
add.addEventListener('click', () => {
const item = `item ${Date.now()}`;
list.innerHTML += `<div data-item="${item}">${item}</div>`;
})
<div id="list"></div>
<button id="add">add</button>
The magic here is we assign a single event handler on the parent, and use closest to figure out what item was clicked. I'm using innerHTML here for simplicity but it should be avoided for security reasons.
A good pattern to use when appropriate is event delegation. It allows following the Don't Repeat Yourself principle, making code maintenance considerably easier and potentially making scripts run significantly faster. And in your case, it avoids the pitfalls of an element being responsible for modifying its own content.
For example:
const container = document.getElementById('container');
container.addEventListener("click", toggleColor); // Events bubble up to ancestors
function toggleColor(event) { // Listeners automatically can access triggering events
const clickedThing = event.target; // Event object has useful properties
if(clickedThing.classList.contains("click-me")){ // Ensures this click interests us
clickedThing.classList.toggle("blue");
}
}
.click-me{ margin: 1em 1.5em; padding 1em 1.5em; }
.blue{ color: blue; }
<div id="container">
<div id="firstDiv" class="click-me">First Div</div>
<div id="firstDiv" class="click-me">Second Div</div>
</div>

Scope issues inside an Event Listener?

The following code basically shows/hides paragraph tags, I'm having to re-declare the paras variable. Is this because I'm dynamically injecting the button into the DOM, or is it to do with scope? How could I better construct this markup?
// vars
var revealContainer = document.querySelector('.reveal-more');
var paras = revealContainer.querySelectorAll('p');
var status = true;
// return
if (paras && paras.length <= 3) return;
// generate show more link
revealContainer.innerHTML += '<button class="button--text reveal-more__btn">Read more</button>';
var revealBtn = revealContainer.querySelector('.reveal-more__btn');
// click event
revealBtn.addEventListener('click', function () {
var paras = revealContainer.querySelectorAll('p');
// toggle show/hide class
for (var i = 0; i < paras.length; i++) {
var p = paras[i];
p.classList.toggle('is-shown');
}
// check status
if (status) {
this.textContent = 'Read less';
status = false;
} else {
this.textContent = 'Read more';
status = true;
}
});
You can use the live HTMLCollection returned by .getElementsByTagName() instead of the static NodeList returned by .querySelectorAll()
The getElementsByTagName method of Document interface returns an HTMLCollection of elements with the given tag name. The complete document is searched, including the root node. The returned HTMLCollection is live, meaning that it updates itself automatically to stay in sync with the DOM tree without having to call document.getElementsByTagName() again.
var paragraphs = document.getElementById("container").getElementsByTagName("p");
console.log(paragraphs.length);
setInterval(function() {
document.getElementById("container").insertAdjacentHTML("beforeend", "<p>p</p>");
}, 1000);
setInterval(function() {
console.log(paragraphs.length);
}, 2000);
<div id="container"></div>
Below is a really simple Snippet that demonstrates delegated events in pure Javascript, instead of using jQuery.
Here you can see I've attached the eventListener to the div with id elements, this will then listen for click events under this, a simple matches is used just in case you have other elements your not interested in..
document.querySelector("#elements").addEventListener("click", (e) => {
if (!e.target.matches('.element')) return
console.log(`Clicked ${e.target.innerText}`);
});
.element {
border: 1px solid black;
margin: 5px;
}
<div id="elements">
<div class="element">1</div>
<div class="element">2</div>
<div class="element">3</div>
<div>Clicking this does nothing.</div>
</div>

JS - Wrap all child elements of div in a wrapper div [duplicate]

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

Set `display: none` recursively except for within a specific element

If I wanted to hide all elements except for those within a <div id="content"> (including div#content itself), I could use the following CSS:
*
{
visibility: hidden !important;
}
div#content, div#content *
{
visibility: visible !important;
}
One thing to note about this solution is that the hidden elements still take up space. Unfortunately, not all elements have the same display attribute, so you cannot simple simply replace visibility above with display.
Using JavaScript, how can I set all elements to that are not within the <div id="#content"> 'family' to display: none?
A general purpose solution to change the style on the fewest objects, but make sure that #content and all it's sub-elements are visible requires an algorithm to traverse up from #content and hide all siblings at each level up without ever hiding an ancestor of #content. Because this starts at #content and goes up, it never hides any elements inside of #content.
function hideAllExcept(id) {
var el = document.getElementById(id);
while (el && el != document.body) {
// go one level up
var parent = el.parentNode;
// get siblings of our ancesotr
var siblings = parent.childNodes;
// loop through the siblings of our ancestor, but skip out actual ancestor
for (var i = 0, len = siblings.length; i < len; i++) {
if (siblings[i] != el && siblings[i].nodeType == 1) {
// only operate on element nodes
siblings[i].style.display = "none";
}
}
el = parent;
}
}
hideAllExcept("content");
Caveat: this first version does not hide text nodes that are siblings of an ancestor of #content (all other text nodes outside of #content are hidden because their parent is hidden). To hide those text nodes too, they would have to get wrapped in a <span> tag so the style could be set on the <span> tag, but I don't know if the OP needs that level of complexity or wants the text nodes wrapped in that way.
For completeness, here's a version that will wrap parent sibling text nodes so they can also be set to display: none. This may or may not be needed depending upon the source HTML:
function hideAllExcept(id) {
var el = document.getElementById(id);
while (el && el != document.body) {
// go one level up
var parent = el.parentNode;
// get siblings of our ancesotr
var siblings = parent.childNodes;
// loop through the siblings of our ancestor, but skip out actual ancestor
for (var i = 0, len = siblings.length; i < len; i++) {
var node = siblings[i];
if (node != el) {
if (node.nodeType == 1) {
// only operate on element nodes
node.style.display = "none";
} else if (node.nodeType == 3) {
// wrap text node in span object so we can hide it
var span = document.createElement("span");
span.style.display = "none";
span.className = "xwrap";
node.parentNode.insertBefore(span, node);
// Be wary of the dynamic siblings nodeList changing
// when we add nodes.
// It actually works here because we add one
// and remove one so the nodeList stays constant.
span.appendChild(node);
}
}
}
el = parent;
}
}
hideAllExcept("content");​
And a working demo: http://jsfiddle.net/jfriend00/yVWDx/
Try this
var al = document.body.getElementsByTagName("*");
for(var i =0;i<al.length;i++)
{
var elm = al[i];
if(elm.parentNode.id != 'content') {
elm.style.display = 'none';
}
}

Pure javascript method to wrap content in a div

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

Categories