Wrap multiple elements in new div without destroying them - Javascript DOM manipulation - javascript

I am trying to 'de-jquery' some code.
I have a div like so:
<div id="main">
<div class="blue"></div>
<div class="green"></div>
<div class="yellow"></div>
</div>
And I wish to insert a wrapper div around all elements except the first. The full number of elements is undetermined, there could be more.
The current solution uses jquery nextAll and wrapAll to produce the following result:
HTML
<div id="main">
<div class="blue"></div>
<div class="wrapper">
<div class="green"></div>
<div class="yellow"></div>
</div>
</div>
jQuery
$(".blue").each(function() {
$(this)
.nextAll()
.wrapAll('<div class="wrapper"></div>');
});
How can I remove all jQuery from this and make it vanilla?
I cannot see any wrap type methods. I could grab the HTML that doesn't have index of [0] and insert it into a new div, then insert that after .blue but that seems messy. Is there a better way?

edit: oh you just want to skip the first item…
skip this solution to the new solution at the bottom.
// this is how you can grab a node.
// alternatively you could use document.querySelectorAll
// (wich will be similar to how $() works)
const blue = document.querySelector('.blue');
// if you desire to use querySelectorAll you can have something similar to
// .each() like: [...document.querySelectorAll('.blue')].map(element => {});
// this is not a necessity but keeps code a little more organized,
// instead of throwing this into line 22.
const nodes = [];
let element = blue;
while(element = element.nextElementSibling) {
nodes.push(element);
}
// let's create a new div
const wrapper = document.createElement('div');
// and add the classname of your desire.
wrapper.classList.add('wrapper');
// now let's iterate over all nodes we stored earlier:
nodes.map(element => wrapper.appendChild(element));
// and append the wrapper to the .main div:
blue.parentNode.appendChild(wrapper);
// and for the fun of it, let's display the outcome:
document.body.appendChild(document.createElement('code')).textContent = blue.parentNode.outerHTML;
div {
padding: 2px;
border: 1px dotted #000;
min-height: 20px;
box-sizing: border-box;
position: relative;
}
<div id="main">
<div class="blue"></div>
<div class="green"></div>
<div class="yellow"></div>
</div>
i just realized you just want to iterate after the first child…
let's try this then:
// let's grab the main element:
const main = document.querySelector('#main');
// you could also do this: document.querySelector('.blue').parentNode;
// now let's grab the children of that node and strip the first one:
const nodes = [...main.children].splice(1);
// now let's create the wrapper div
const wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
// and append all children:
nodes.map(node => wrapper.appendChild(node));
// and ofc add the wrapper to the container:
main.appendChild(wrapper);
div {
padding: 2px;
border: 1px dotted #000;
min-height: 20px;
box-sizing: border-box;
position: relative;
}
<div id="main">
<div class="blue"></div>
<div class="green"></div>
<div class="yellow"></div>
</div>

Try below code.
var allBlues = document.querySelectorAll(".blue");
var divWrapper = document.createElement("div");
divWrapper.className = "wrapper";
for(var i = 0; i < allBlues.length; i++){
// Iterate through all the siblings.
var tempEle;
while(tempEle = allBlues[i].nextElementSibling){
divWrapper.appendChild(tempEle);
}
}
main.appendChild(divWrapper);
.blue{
}
<div id="main">
<div class="blue"></div>
<div class="green"></div>
<div class="yellow"></div>
</div>

Related

How to get element html() and edit or remove some classes/elemetns

Im trying to get the html of container and remove some useless element and classes before send to Database but i dont know why this is not working
$('button').click(function(){
var html = $('.wrapper-container').html();
$(html).find('.single-col').removeClass('.single-col')
$(html).find('p').remove();
console.log(html);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="wrapper-container">
<div class="single-col">Hello</div>
<div class="single-col">World</div>
<p>Junk text</p>
</div>
<button type="button">Clear html</button>
$('button').click(function(){
var container = $('.wrapper-container').clone(); // operate on a clone to not modify the real thing
container.find('.single-col').removeClass('single-col'); // not .single-col in removeClass
container.find('p').remove();
console.log(container.html());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="wrapper-container">
<div class="single-col">Hello</div>
<div class="single-col">World</div>
<p>Junk text</p>
</div>
<button type="button">Clear html</button>
I'd suggest the following approach, instead of trying to modify a string use a cloned element, or document fragment:
$('button').click(function() {
// using 'let' instead of 'var' to scope the variable to
// local block; and using the .clone() method, with
// the (Boolean) true argument to include the descendant
// elements (but without cloning event-handlers):
let clone = $('.wrapper-container').clone(true);
// here we find the '.single-col' elements in the
// cloned node, and remove that class (this doesn't
// remove the class attribute, just the class-name):
clone.find('.single-col').removeClass('single-col');
// here we find the <p> element(s), and remove them:
clone.find('p').remove();
// here we append the cloned element, using the append()
// method, to the #resultingDOM element:
$('#resultingDOM').append(clone);
// here we update the text of the #resultingHTML
// element, using the text() method, to the innerHTML
// of the cloned .wrapper-container elememt:
$('#resultingHTML').text(
clone.html()
);
console.log(clone);
})
*,
::before,
::after {
box-sizing: border-box;
font-size: 1rem;
line-height: 1.5;
margin: 0;
padding: 0;
}
.wrapper-container {
margin: 1em auto;
width: 80vw;
}
.single-col {
--color: #f90;
}
.wrapper-container :not(div) {
--color: lime;
}
.wrapper-container>* {
background-image: linear-gradient(90deg, var(--color, aqua), #fffa);
}
.wrapper-container::before,
.wrapper-container>*[class]:not([class=""])::before {
content: '(.' attr(class) ')';
}
#resultingHTML {
white-space: pre-wrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="wrapper-container">
<div class="single-col">Hello</div>
<div class="single-col">World</div>
<p>Junk text</p>
</div>
<button type="button">Clear html</button>
<!-- two new elements to show the results, one is the DOM
produced by jQuery manipulation of the clone, the
other is to show the resulting HTML string from that
DOM manipulation: -->
<div id="resultingDOM"></div>
<div id="resultingHTML"></div>
References:
clone().
find().
html().
removeClass().
text().

Generate an HTML wrapper after opening and before closing parent tags

I'd like to generate a wrapper that's a child of a shared class, but it's the immediate parent of the whatever HTML markup is added in. It will save time creating a bunch of wrapper divs and the purpose of this is to create specific layouts, backgrounds, etc.
Simply put, for example, any class of .page should generate the wrapper but also generate around whatever markup that goes inside it. Would need something like this:
<div class="page">
<div class="wrapper"> <!-- GENERATED WRAPPER -->
<!-- This is where any markup can go -->
</div><!-- GENERATED WRAPPER -->
</div>
I wasn't sure if PHP would be a better, more secure method but the closest thing I could find was: See Michal's answer. Not sure how I would apply a loop to get this to work, any ideas would help. Thanks. Also, pure JS only please.
Although better methods may exist, this should work on every current browser.
It works by finding every .page element, creating an initially empty wrapper element, and then appending each child node of the original element into the wrapper (nb: the append operation causes the child to be removed from its original parent, after which the next remaining sibling automatically becomes the first child).
Once done, it finally inserts the wrapper element (which now contains all of the original children) into the DOM as the sole new child.
const pages = document.querySelectorAll('.page');
for (const p of pages) {
const wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
while (p.firstChild) {
wrapper.appendChild(p.firstChild);
}
p.appendChild(wrapper);
}
.page {
border: 1px solid black;
margin: 4px;
}
.wrapper {
border: 1px solid red;
margin: 4px;
}
<div class="page">
<div>Page 1</div>
<p>Para 1</p>
<p>Para 2</p>
</div>
<div class="page">
<div>Page 2</div>
<p>Para 1</p>
<p>Para 2</p>
</div>
const pages = document.querySelectorAll('.page');
const createWrapper = () => {
const parser = new DOMParser();
const doc = parser.parseFromString('<div class="wrapper"><nav><ul><li>Link</li></ul></nav></div>', 'text/html');
return doc.body;
}
pages.forEach(el => {
const wrapper = createWrapper();
const newMarkup = wrapper.innerHTML = wrapper.innerHTML + el.innerHTML;
el.innerHTML = wrapper.innerHTML;
});

Insert wrapper around div without an ID or class

I'm trying to insert a wrapper around two divs, one with a dynamically generated ID. The div with the random ID is dynamically generated. No matter what I try, the wrapper is getting inserted after the target div though.
Before wrapper
<div id="search">Search</div>
<div id="234234">Unknown</div>
<div id="list">List</div>
After wrapper
<div id="search">Search</div>
<div id="wrapper">
<div id="234234">Unknown</div>
<div id="list">List</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="search">Search</div>
<div id="wrapper">
<div id="234234">Unknown</div>
<div id="list">List</div>
</div>
EDIT
I have decided to use CSS to reposition the elements so that I no longer need the wrapper.
Simply wrap the children!
$("#wrapper").children().wrapAll("<div class='wrapper'/>")
#wrapper { padding: 20px; }
.wrapper { background-color: gold; outline: 2px solid red; }
<div id="wrapper">
<div id="123456">has whatever ID</div>
<div id="list">has id</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
Or, to target elements that actually have any ID attribute use the "[id]" selector:
$("#wrapper").children("[id]").wrapAll("<div class='wrapper'/>")
#wrapper { padding: 20px; }
.wrapper { background-color: gold; outline: 2px solid red; }
<div id="wrapper">
<div id="list123456">has id</div>
<div id="list">has id</div>
<div>Foo bar</div>
<div id="list234">has id</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
Before Edit:
Use the :not([id]) or / and [id=""] selectors if you want to target "no ID attribute" / or / "empty ID attribute" respectively:
$("#wrapper").children("div:not([id]), div[id='']").wrap("<div class='wrapper'/>")
#wrapper { padding: 20px; }
.wrapper { background-color: gold; outline: 2px solid red; }
<div id="wrapper">
<div id="">has no id</div>
<div>has no attribute id</div>
<div id="list">has id</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
If you do not know the element id, you can still use a class attribute to access it. Give the element with the random id (and any other elements you want to move) a class attribute so you can access via JavaScript and manipulate the DOM. You can do it simply with no jQuery, like this:
// Create the `div.wrapper` element
const wrapperEl = document.createElement('div');
wrapperEl.classList.add('wrapper');
// Create a reference to each element you want to make a child of `div.wrapper`
const childEls = document.querySelectorAll('.wrapped');
// Move the elements from `body` to `div.wrapper` parent
wrapperEl.replaceChildren(...childEls);
// Append the `div.wrapper` to the body element
const bodyEl = document.querySelector('body')
bodyEl.appendChild(wrapperEl);
console.log(wrapperEl.outerHTML);
<div id="search">Search</div>
<div id="234234" class="wrapped">Unknown</div>
<div id="list" class="wrapped">List</div>
I've logged the wrapperEl HTML in the console, but you can also inspect the HTML in your dev tools "Elements" tab to see the wrapper.
Reference: https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/replaceChildren#transferring_nodes_between_parents

How do I append an existing html element to another element?

I want to append multiple copies of div with id hello into the div with id container. How do I do this using javascript?
<div id="container">
</div>
<div id="hello">
<p>Hello</p>
</div>
Using cloneNode and appendChild.
Since ids should be unique in a document, I switched things to use a class hello instead.
function makeCopy() {
const target = document.getElementById("container");
const source = document.querySelector(".hello");
const clone = source.cloneNode(true);
target.appendChild(clone);
}
.hello {
padding: 3px;
margin: 3px;
border: 1px dotted orange;
}
<div id="container">
This is the container.
</div>
<button onclick="makeCopy()">Add a clone of hello above</button>
<div class="hello">
<p>Hello</p>
</div>
https://www.w3schools.com/jsref/met_node_appendchild.asp
check this out, it will solve your problem, BUT use a class for the tag not an ID. IDs are unique, a class is for multiple elements.
you can call class in CSS like this
.className {
/*CODE*/
}
you can use append() method:
$(document).on('click', function(){
$('#container').append( $('#hello') );
});

Better ways of inserting simple HTML into a div using JavaScript

I'm always seeming to have to insert HTML into a load of divs using jQuery, but I prefer JavaScrip and wonder if anybody has any better ways of doing it. See the typical example below:
let boxes = document.querySelectorAll('.main-editor-output');
for (let box of boxes) {
console.log(box);
$( "<p>Test</p>" ).prependTo(box);
}
.main-editor-output {
border: 1px solid black;
margin-top: 10px;
min-height: 1em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<div id='parent'>
<div class="main-editor-output"></div>
<div class="main-editor-output"></div>
</div>
Here I'm just using a simple line of jQuery to insert HTML into the test divs. I could do it using javascript, but that would require too many lines of code. Does anyone have any better ways of doing this?
Thanks for any ideas!
Codepen: https://codepen.io/ns91/pen/GRKGwZP
you can use insertAdjacentHTML
https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
in your example it would be box.insertAdjacentHTML('afterbegin', '<p>Test</p>');
demo:
let boxes = document.querySelectorAll('.main-editor-output');
for (let box of boxes) {
console.log(box);
box.insertAdjacentHTML('afterbegin', '<p>Test</p>');
}
.main-editor-output {
border: 1px solid black;
margin-top: 10px;
min-height: 1em;
}
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<div id='parent'>
<div class="main-editor-output">
Existing text
</div>
<div class="main-editor-output">
</div>
</div>
In Vanilla JavaScript you can:
create an item with createElement
append it with appendChild
prepend it with insertBefore
For example, the following code will create two inputs, assign them an id and a value, then append the first and prepend the second inside a div with id container:
let container = document.getElementById('container');
let input1 = document.createElement('input');
input1.id = 'input1';
input1.value = 'I am input 1!';
container.appendChild(input1);
let input2 = document.createElement('input');
input2.id = 'input2';
input2.value = 'I am input 2!';
container.insertBefore(input2, input1);
<div id="container"></div>
You can use $element.innerHTML, which is a native JavaScript property of elements.
It works similar to the jQuery element method $element.html().
<div id="test">Pre Text</div>
<script> document.querySelector('#test').innerHTML = 'Post Text'; </script>
I don't know if it's exactly what you're looking for, but in any case you can use document.createElement to create the child element, and add it to the parent element using the prepend method:
let boxes = document.querySelectorAll('.main-editor-output');
for (let box of boxes) {
console.log(box);
let element = document.createElement("p");
element.append("Test");
box.prepend(element);
}
.main-editor-output {
border: 1px solid black;
margin-top: 10px;
min-height: 1em;
}
<head>
</head>
<div id='parent'>
<div class="main-editor-output">
</div>
<div class="main-editor-output">
</div>
</div>

Categories