I cannot seem to find what should be a basic function of JavaScript. I want to move an element up one position or down one position by a button click. jQuery and other proprietary libraries are not an option.
function moveDown()
{
if (document.getElementById('CMSeditingNode'))
{
var node = document.getElementById('CMSeditingNode'),
parent = node.parentNode,
nextNode = node.nextSibling,
secondNode = nextNode.nextSibling,
oldChild = parent.removeChild(node);
parent.insertBefore(oldChild, secondNode);
}
}
The answer worked perfectly and I have add to this post a supplement for insertAfter().
The methods/properties you're looking for are parentNode, previousSibling, nextSibling, removeChild, insertBefore, and insertAfter. A snippet might look like:
var node = document.getElementById('somenode'),
parent = node.parentNode,
prev = node.previousSibling,
oldChild = parent.removeChild(node);
parent.insertBefore( oldChild, prev );
Just for comparison, the (complete) jQuery for this would be:
var $node = $('#somenode'),
$node.prev().before($node);
Related
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 always used jQuery before, but I want to switch the following to native javascript for better performance of the website.
var first = $('ul li:first');
var first = $('ul li:last');
$(last).before(first);
$(first).after(last);
From: http://clubmate.fi/append-and-prepend-elements-with-pure-javascript/
Before (prepend):
var el = document.getElementById('thingy'),
elChild = document.createElement('div');
elChild.innerHTML = 'Content';
// Prepend it
el.insertBefore(elChild, el.firstChild);
After (append):
// Grab an element
var el = document.getElementById('thingy'),
// Make a new div
elChild = document.createElement('div');
// Give the new div some content
elChild.innerHTML = 'Content';
// Jug it into the parent element
el.appendChild(elChild);
To get the first and last li:
var lis = document.getElementById("id-of-ul").getElementsByTagName("li"),
first = lis[0],
last = lis[lis.length -1];
if your ul doesn't have an id, you can always use getElementsByTagName("ul") and figure out its index but I would advise adding an id
I guess you are looking for:
Element.insertAdjacentHTML(position, text);
Where position is:
'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.
And text is a HTML string.
Doc # MDN
You can use insertBefore():
var node = document.getElementById('id');
node.parentNode.insertBefore('something', node);
Documentation: insertBefore()
There is no insertAfter method. It can be emulated by combining the insertBefore method with nextSibling():
node.parentNode.insertBefore('something', node.nextSibling);
I figured I would get fancy and use vanilla JavaScript during a jQuery event. The idea is that on click of a heading, I want to slide up a div (which works) and replace the tag clicked on to a larger heading.
From what I've read around, this can be caused by the parentNode referencing an element that's not the actual parent, but after checking it appears to be selecting the element that's directly above it.
So... here's the code!
HTML (in Jade)
.policy-container
h6.policy-heading Policies
.policy-list
.content-we-are-hiding
.not-actually-important
jQuery
$('.policy-heading').click(function() {
var self = this;
if (this.classList.contains('closed')) {
$(this).next().slideDown(300);
this.parentNode.replaceChild(self, '<h6 class="policy-heading">Policies</h6>');
} else {
$(this).next().slideUp(300);
this.parentNode.replaceChild(self, '<h2 class="policy-heading closed">Policies</h2>');
}
});
Everything seems pretty standard. Luckily I can just take care of this with jQuery, however I'd rather be using vanilla JS here. Any ideas why this isn't working?
As has been pointed out, replaceChild takes two nodes.
The following will work with native JS wrapped inside jQuery, as you've specified:
$('.policy-heading').click(function () {
var self = this,
h2 = document.createElement('h2'),
h6 = document.createElement('h6');
h2.class = "policy-heading closed";
h2.innerHTML = "Policies";
h6.class = "policy-heading";
h6.innerHTML = "Policies";
if (this.classList.contains('closed')) {
$(this).next().slideDown(300);
this.parentNode.replaceChild(h6, self);
} else {
$(this).next().slideUp(300);
this.parentNode.replaceChild(h2, self);
}
});
replaceChild takes two nodes, you are giving it a node and a string.
It looks like you'd be much better off just sticking with jQuery and using toggle functions for the sliding and class change.
try this :
.click(function(this)
you also need some debugging to understand what is going on I would advice you to use :
console.log(this)
use this :
el = document.createElement('h6');
el.class = "policy-heading";
el.innerHTML = "Policies";
this.parentNode.replaceChild(self, el);
As everyone pointed out, .replaceChild accepts two DOM elements, rather than the string like I was using. I also had its arguments backwards, the first is for the new element, the second is the replaced element.
Example code that works
$('.policy-container').on('click', '.policy-heading', function() {
var self = this,
newElement;
if (this.classList.contains('closed')) {
newElement = document.createElement( 'h6' );
newElement.classList.add('policy-heading');
newElement.innerHTML = 'Policies';
} else {
newElement = document.createElement( 'h2' );
newElement.classList.add('policy-heading');
newElement.classList.add('closed');
newElement.innerHTML = 'Policies';
}
$(this).next().slideDown(300, function() {
self.parentNode.replaceChild( newElement, self );
});
});
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
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;