I have this structure:
<div clas="page_cat_list">
<div class="page_cat_row">
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="clear_fix"></div>
</div>
<div class="page_cat_row">
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="clear_fix"></div>
</div>
</div>
and I need it to look like this:
<div clas="page_cat_list">
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
</div>
I can insert JavaScript in the HTML, but I can not change these elements directly, because it's generated by some kind of function.
You could arrive with this simple js approach:
var container = document.querySelector('.page_cat_list');
var contents = container.querySelectorAll('.page_cat_row');
var newContent = '';
[].forEach.call(contents, function(cont) {
newContent = newContent + cont.innerHTML;
})
container.innerHTML = newContent;
<div class="page_cat_list">
<div class="page_cat_row">
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="clear_fix"></div>
</div>
<div class="page_cat_row">
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="clear_fix"></div>
</div>
</div>
Loop over the elements and than loop over the children appending them to the parent element.
(function() {
var wrappers = document.querySelectorAll(".page_cat_row"); //grab the parent elements
//Loop over the collection
for (var i = wrappers.length - 1; i >= 0; i--) {
//get the element and its parent
var wrapperToBeRemoved = wrappers[i],
parentNode = wrapperToBeRemoved.parentNode
//loop over the children until they are all removed
while (wrapperToBeRemoved.firstChild) {
parentNode.insertBefore(wrapperToBeRemoved.firstChild,
wrapperToBeRemoved);
}
//remove the wrapper from the collection
parentNode.removeChild(wrapperToBeRemoved);
}
}());
.page_cat_row {
background-color: red;
}
<div clas="page_cat_list">
<div class="page_cat_row">
<div class="page_cat_item">1...</div>
<div class="page_cat_item">2...</div>
<div class="page_cat_item">3...</div>
<div class="clear_fix"></div>
</div>
<div class="page_cat_row">
<div class="page_cat_item">4...</div>
<div class="page_cat_item">5...</div>
<div class="page_cat_item">6...</div>
<div class="clear_fix"></div>
</div>
</div>
You can easily do this with a combination of detach() and html() in JQuery:
$(document).ready(function(){
var ele = $('.page_cat_row').children().detach();
$('.page_cat_row').remove();
$('.page_cat_list').html(ele);
});
See the JSFiddle: https://jsfiddle.net/w6gkf23d/
With this, you don't need to directly loop anything as JQuery does it all for you.
Note that you will still need to set the clear_fix class to display:none.
Simplest way is to use unwrap.
$('.page_cat_item').unwrap();
I think this is the easiest way to do this.
Move the first childNode to the parent until the wrapper element has children.
If you don't have text between the elements, you can use wrapper.children.length instead, because it's faster in that case.
function removeElement(element) {
element.parentElement.removeChild(element);
}
function removeWrapper(wrapper) {
while (wrapper.childNodes.length) {
wrapper.parentElement.appendChild(wrapper.childNodes[0]);
}
wrapper.parentElement.removeChild(wrapper);
}
var clrFix = document.querySelectorAll('.page_cat_row > .clear_fix');
for (var i = 0; i < clrFix.length; i++) {
removeElement(clrFix[i]);
}
var cat_rows = document.querySelectorAll('.page_cat_list > .page_cat_row');
for (var i = 0; i < cat_rows.length; i++) {
removeWrapper(cat_rows[i]);
}
.page_cat_row {
color: red;
}
<div class="page_cat_list">
<div class="page_cat_row">
<div class="page_cat_item">1...</div>
<div class="page_cat_item">2...</div>
<div class="page_cat_item">3...</div>
<div class="clear_fix">fix</div>
</div>
<div class="page_cat_row">
<div class="page_cat_item">4...</div>
<div class="page_cat_item">5...</div>
<div class="page_cat_item">6...</div>
<div class="clear_fix">fix</div>
</div>
</div>
JSFiddle
$( "#divid" ).load(function() {
$( this ).removeClass( "page_cat_row" );
});
Related
If the element has children insert a new div "i-new-element". parent2 and parent3 get a new div.
child3-parent3 has children but doesn't get a new giant. Why?
How can I make it possible for children who have children to get a new div?
it should look like:
<div id="child3-parent3">
<div id="i-new-element"></div>
<div id="child3"></div>
<div id="child4"></div>
</div>
var container = document.getElementById("container").querySelectorAll("#container > *");
container.forEach(function(div) {
{
if (div.hasChildNodes()) {
let parentElement = div;
let theFirstChild = parentElement.firstChild;
let newElement = document.createElement("div")
newElement.id = "i-new-eleemnt"
parentElement.insertBefore(newElement, theFirstChild)
}
}
});
<div id="container">
<div id="parent1"></div>
<div id="parent2">
<div id="child1"></div>
<div id="child2"></div>
<div id="child3-parent3">
<div id="child3"></div>
<div id="child4"></div>
</div>
</div>
<div id="parent3">
<div id="child5"></div>
<div id="child6"></div>
</div>
</div>
Using
var container = document.getElementById("container");
var elements = container.querySelectorAll(":scope *");
instead of
var container = document.getElementById("container").querySelectorAll("#container > *");
should fix your problem.
var container = document.getElementById("container");
var elements = container.querySelectorAll(":scope *");
elements.forEach(function(div){
{
if (div.hasChildNodes()) {
let parentElement = div;
let theFirstChild = parentElement.firstChild;
let newElement = document.createElement("div")
newElement.id = "i-new-eleemnt"
parentElement.insertBefore(newElement, theFirstChild)
}
}
});
<div id="container">
<div id="parent1"></div>
<div id="parent2">
<div id="child1"></div>
<div id="child2"></div>
<div id="child3-parent3">
<div id="child3"></div>
<div id="child4"></div>
</div>
</div>
<div id="parent3">
<div id="child5"></div>
<div id="child6"></div>
</div>
</div>
Edit: Further information about the :scope CSS pseudo-class can be found here and here.
I believe your problem is the following, even it is not described well in your sample HTML:
<div id="parent1"></div> per instance is actually not an empty node but contains some text i.e. <div id="parent1">some text here</div>.
You might believe or not, but node.hasChildNodes() will count any text as a child node (text node, nodeType = 3), so it will always return true is any text is present.
To avoid that, you can filter the text nodes first or just use this workaround:
Replace this line:
if (div.hasChildNodes()) {
with that line:
if (div.children.length) {
children property is not counting text nodes.
That's all you have to do, I believe.
var container = document.querySelectorAll("#container > *");
container.forEach(function(div) {
{
if (div.children.length) {
let parentElement = div;
let theFirstChild = parentElement.children[0];
let newElement = document.createElement("div")
newElement.id = "i-new-eleemnt";
newElement.innerHTML = 'i-new-eleemnt'
parentElement.insertBefore(newElement, theFirstChild);
}
}
});
<div id="container">
<div id="parent1">parent1</div>
<div id="parent2">parent2
<div id="child1">child1</div>
<div id="child2">child2</div>
<div id="child3-parent3">child3-parent3
<div id="child3">child3</div>
<div id="child4">child4</div>
</div>
</div>
<div id="parent3">parent3
<div id="child5">child5</div>
<div id="child6">child6</div>
</div>
</div>
I want to wrap each item of the container in a div. When I loop through HTMLCollection, some elements are accessed multiple times while others are left out
HTML
<div class="container">
<div class="item_1"></div>
<div class="item_2"></div>
<div class="item_3"></div>
<div class="item_4"></div>
<div class="item_5"></div>
<div class="item_6"></div>
<div class="item_7"></div>
<div class="item_8"></div>
<div class="item_9"></div>
</div>
JS
const container = document.querySelector('.container');
const items = container.children;
for(let i = 0; i < items.length; i++) {
const wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
wrapper.appendChild(items[i]);
container.appendChild(wrapper);
}
Looping directly through HTMLCollection gives this bizarre result
<div class="container">
<div class="item_2"></div>
<div class="item_4"></div>
<div class="item_6"></div>
<div class="item_8"></div>
<div class="wrapper">
<div class="item_1"></div>
</div>
<div class="wrapper">
<div class="item_5"></div>
</div>
<div class="wrapper">
<div class="item_9"></div>
</div>
<div class="wrapper">
<div class="wrapper">
<div class="item_7"></div>
</div>
</div>
<div class="wrapper">
<div class="wrapper">
<div class="wrapper">
<div class="wrapper">
<div class="item_3"></div>
</div>
</div>
</div>
</div>
</div>
problem gets solved when I convert HTMLCollection to an Array
const items = Array.from(container.children);
I can't understand what causes such behavior
You were iterating the container.children list which you were also changing during the iterations. This messed up the iteration. You can solve this, as you mentioned yourself, by converting the container.children to an array because then you are not iterating over the live container.children list but over an array copy of that. This copy is still referring to the correct child elements so they are moved correctly with the appendChild() function.
As an alternative you can use the querySelecterAll() to retrieve all the elements you want to wrap.
const container = document.querySelector('.container');
const items = container.querySelectorAll('.container > *');
for(let i = 0; i < items.length; i++) {
const wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
wrapper.appendChild(items[i]);
container.appendChild(wrapper);
}
.wrapper {
background-color: red;
}
<div class="container">
<div class="item_1">1</div>
<div class="item_2">2</div>
<div class="item_3">3</div>
<div class="item_4">4</div>
<div class="item_5">5</div>
<div class="item_6">6</div>
<div class="item_7">7</div>
<div class="item_8">8</div>
<div class="item_9">9</div>
</div>
I am trying to add a div with class name 'wrapped' around 2 divs with innerHTML 'one' and 'two'
<div class='blk'>one</div>
<div class='blk'>two</div>
Here I am trying to use the first and second 'clearfix' class element and use it as a selector to wrap the contents between them with a div. What I understand from the below code is wrapAll will wrap only the matched elements. I am trying to figure out what functionality in jquery will help me to wrap one and two with a parent div.
var arr = $('.clearfix');
$(arr[0], arr[1]).wrapAll('<div class="wrapped"/>');
.wrapped {
background-color: 'red';
}
.hidden {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div class='clearfix'></div>
<div class='blk'>one</div>
<div class='blk'>two</div>
<div class='clearfix'></div>
<div class='blk'>3</div>
<div class='blk'>4</div>
<div class='clearfix'></div>
</div>
My expected output is :
<div>
<div class='clearfix'></div>
<div class='wrapped'>
<div class='blk'>one</div>
<div class='blk'>two</div>
</div>
<div class='clearfix'></div>
<div class='blk'>3</div>
<div class='blk'>4</div>
<div class='clearfix'></div>
</div>
If I understand the question correctly, you're wanting to wrap a div (with class wrapped) around the two elements (with class blk) that contain the text content; "one" and "two".
One way to achieve this would be via the :eq selector - this allows you to isolate specific elements for the .blk class selector (ie the first and second), and than apply wrapping to those only:
/* Select the first and second elements that match the .blk selector
and apply wrapped div to those only */
$('.blk:eq(0), .blk:eq(1)').wrapAll('<div class="wrapped"/>');
.wrapped{
background-color: red; /* Fixed syntax error here too :) */
}
.hidden{
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div class='clearfix'></div>
<div class='blk'>one</div>
<div class='blk'>two</div>
<div class='clearfix'></div>
<div class='blk'>3</div>
<div class='blk'>4</div>
<div class='clearfix'></div>
</div>
You can try with .filter()
var arr = $('.blk');
arr.filter((i, el) => i < 2).wrapAll(`<div class="wrapped"></div>`);
.wrapped{
background-color: red;
}
.hidden{
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div class='clearfix'></div>
<div class='blk'>one</div>
<div class='blk'>two</div>
<div class='clearfix'></div>
<div class='blk'>3</div>
<div class='blk'>4</div>
</div>
var clearfixParent = $('.clearfix').parent();
var wrappedEle = 0;
$(clearfixParent).find( 'div' ).each(function(){
if( $(this).hasClass( 'clearfix' ) ) {
wrappedEle += 1;
$(this).after('<div class="wrapped"/>');
} else {
$(this).appendTo( '.wrapped:eq(' + ( wrappedEle - 1 ) + ')' );
}
});
.wrapped {
background-color: 'red';
}
.hidden {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div class='clearfix'></div>
<div class='blk'>one</div>
<div class='blk'>two</div>
<div class='clearfix'></div>
<div class='blk'>3</div>
<div class='blk'>4</div>
<div class='clearfix'></div>
</div>
You don't need jQuery for this, you can use the DOM API.
const appendSelectedSiblings = selector => target => {
while (target.nextElementSibling && target.nextElementSibling.matches(selector))
target.appendChild(target.nextElementSibling)
}
document.querySelectorAll('.clearfix').forEach(appendSelectedSiblings('.blk'))
.clearfix > .blk { background: green };
<div id="root">
<div class='clearfix'></div>
<div class='blk'>one</div>
<div class='blk'>two</div>
<div class='clearfix'></div>
<div class='blk'>3</div>
<div class='blk'>4</div>
<div class='clearfix'></div>
</div>
From what I understood with the markup you included, you want to enclose/group all the divs with class BLKs that are beside each other.
I made a snippet that will be able to do this dynamically, without specifying the index of your wrap all.
Feel free to update if you could make the code more efficient.
var divs = $(".scan").find("div");
var toEnclose = [];
var continueEnclose;
var latestArray = [];
divs.each(function(i) {
if ($(this).hasClass("clearfix")) {
if (latestArray.length != 0) {
toEnclose.push(latestArray);
latestArray = [];
}
}
if ($(this).hasClass("blk")) {
latestArray.push(i);
}
});
if (latestArray.length != 0) {
toEnclose.push(latestArray);
latestArray = [];
}
var enclose;
var mix = [];
$.each(toEnclose, function(i, k) {
$.each($(this), function(i2, k2) {
if (i != 0) {
k2 += i;
}
mix.push(".scan div:eq(" + k2 + ")");
});
enclose = mix.join(",");
// console.log(enclose);
$(enclose).wrapAll($("<div class='wrapped'></div>"));
mix = [];
});
.wrapped {
background-color: red;
margin-bottom: 5px;
}
.hidden {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="scan">
<div class='clearfix'></div>
<div class='blk'>one</div>
<div class='blk'>two</div>
<div class='clearfix'></div>
<div class='blk'>3</div>
<div class='blk'>4</div>
<div class='clearfix'></div>
<div class='blk'>5</div>
<div class='blk'>6</div>
<div class='blk'>7</div>
</div>
I have HTML File:
<div class="um-field-area">
<div class="um-field-value">Value1</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value2</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value3</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value4</div>
</div>
<button onclick="Function()">Whatever</button>
<div id="result"></div>
I'd like my function to take values from all four divs with class "um-field-value"
<div class="um-field-value">Value1</div>
And past them in Div "result"
Essentially, I want a script to simply copy values given in class um-field-value and paste it in a "result" div. I tried following:
function Function() {
var x = document.getElementsByClassName("um-field-value");
document.getElementsById('result').innerHTML = x;
}
But that doesn't work at all.
I am somewhat new to coding so I am not entirely sure if it is even possible. Googled for over an hour but couldn't find any solutions.
document.getElementsByClassName gets the HTML nodes themselves but then you have to extract the values from within the HTML nodes, combine them, and set that to your result div. Example snippet below:
function myFunction() {
var valueNodes = Array.prototype.slice.call(document.getElementsByClassName("um-field-value"));
var values = valueNodes.map(valueNode => valueNode.innerHTML);
var result = values.join(' ');
document.getElementById('result').innerHTML = result;
}
<div class="um-field-area">
<div class="um-field-value">Value1</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value2</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value3</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value4</div>
</div>
<button onclick="myFunction()">Whatever</button>
<div id="result"></div>
Use querySelectorAll to get all the dom with this class um-field-value and iterate over that to get the innerHTML
There is a typo in your code.It is getElementById instead of getElementsById. There is an extra s
function Function() {
var x = document.querySelectorAll(".um-field-value");
let result = '';
for (var y = 0; y < x.length; y++) {
result += x[y].innerHTML;
}
document.getElementById('result').innerHTML = result;
}
<div class="um-field-area">
<div class="um-field-value">Value1</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value2</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value3</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value4</div>
</div>
<button onclick="Function()">Whatever</button>
<div id="result"></div>
You are on the right track. document.getElementsByClassName will return a NodeList. You need to get the innerText for each of the elements in that list. Depending on the browser you can either use forEach or a regular for loop to iterate over the list.
function Function() {
var fieldsList = document.getElementsByClassName("um-field-value");
var fieldValues = [];
fieldsList.forEach(function(field) { fieldValues.push(field.innerText) });
document.getElementsById('result').innerHTML = fieldValues.join(", ");
}
This is a simple and readable solution that uses a loop to get the text inside each element and add it to a string. getElementsByClassName returns an array of all elements found, so a loop is needed to get the text inside each with textContent.
function Function() {
var result = '';
var fields = document.getElementsByClassName("um-field-value");
for (var i=0; i<fields.length; i++) {
result += fields[i].textContent + '\n';
}
document.getElementById('result').innerHTML = result;
}
<div class="um-field-area">
<div class="um-field-value">Value1</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value2</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value3</div>
</div>
<div class="um-field-area">
<div class="um-field-value">Value4</div>
</div>
<button onclick="Function()">Whatever</button>
<div id="result"></div>
Here is my HTML
<div id="subscriptionContainer">
<div class="subscription">
<div class="subs-btn">
<div class="subscribed">Subscribed</div>
</div>
</div>
<div class="subscription">
<div class="subs-btn">
<div class="btnGetit">Get It</div>
</div>
</div>
<div class="subscription">
<div class="subs-btn">
<div class="subscribed">Subscribed</div>
</div>
</div>
<div class="subscription">
<div class="subs-btn">
<div class="btnGetit">Get It</div>
</div>
</div>
<div class="subscription">
<div class="subs-btn">
<div class="subscribed">Subscribed</div>
</div>
</div>
<div class="subscription">
<div class="subs-btn">
<div class="btnGetit">Get It</div>
</div>
</div>
</div>
Now I want to rearrange the "subscription" divs withing the "subscriptionContainer" such that the divs that have class = "subscribed" should be moved to bottom and the divs having class = "btnGetit" should all be moved up. Please give solutions in Javascript or JQuery.
var $wrap = $('#subscriptionContainer');
$wrap.find('.subscribed').parents('.subscription').appendTo( $wrap );
Demo: http://jsbin.com/ebudux/2/edit
Here is a pure JavaScript implementation that I finally worked out
var container = document.getElementById('subscriptionContainer');
var allDivs = container.getElementsByTagName('div');
var subsProds = new Array();
var count = 0;
for (i = 0; i < allDivs.length; i++)
{
var currentElement = allDivs[i];
if (currentElement.className == 'subscribed') {
subsProds[count] = currentElement.parentNode.parentNode;
container.removeChild(currentElement.parentNode.parentNode);
count++;
}
}
for (i = 0; i < subsProds.length; i++)
container.appendChild(subsProds[i]);
var $wrap = $('#subscriptionContainer');
$wrap.find('.subscription').children('.subscribed').appendTo( $wrap );