Change child element of array item (syntax) - javascript

I have a few divs which are using the same class.
Inside the divs are three more divs with identical classes.
<div class="plane">
<div class="win1">Lorem ipsum</div>
<div class="win2">Dolor sit</div>
<div class="win3">amet.</div>
</div>
<div class="plane">
<div class="win1">Lorem ipsum</div>
<div class="win2">Dolor sit</div>
<div class="win3">amet.</div>
</div>
var allPlanes = $('.plane');
for (var i = 0; i < allPlanes.length; i++) {
var onePlane = allPlanes[i];
var baseHeight = 10;
$(onePlane + " .win1").css("height", parseInt(baseHeight*1));
$(onePlane + " .win2").css("height", parseInt(baseHeight*2));
$(onePlane + " .win3").css("height", parseInt(baseHeight*3));
}
(Don't mind about the names. It's just an example...)
Now I made an array with the outside divs and I can select the single divs inside. But I did not get the right syntax for the child divs inside.
Can anyone help?
My Fiddle: http://jsfiddle.net/SchweizerSchoggi/559xvww6/

Change you script to this:
var allPlanes = $('.plane');
var baseHeight = 10;
$(".plane > .win1").css("height", parseInt(baseHeight*1)+"px");
$(".plane > .win2").css("height", parseInt(baseHeight*2)+"px");
$(".plane > .win3").css("height", parseInt(baseHeight*3)+"px");
You don't need the for loop in such a case.
A prettier way:
var baseHeight = 10;
for (var i = 1; i <= 3; i++) {
$(".plane > .win"+i).css("height", parseInt(baseHeight*i)+"px");
}
http://jsfiddle.net/559xvww6/3/
If you don't want to use a for loop and want to dinamically configure from an array:
var baseHeight = 10;
$.map([1,2,3], function(i) {
$(".plane > .win"+i).css("height", parseInt(baseHeight*i)+"px");
});
http://jsfiddle.net/559xvww6/10/
Edit:: Just a side note: all these approachs are valid, but that doesn't mean that they are the best / most efficient ones. Feel free to use the one you like the most, understand it and try to use it or adapt it to your very personal situation. The "easiest" approach is surely the first one, but it is also the longest one.

isn't this one is better:
var base = 10;
$('.plane > div').css('height', function(){
return base*($(this).index()+1)
});
.plane {
background-color: #ccc;
border: solid 1px #cdcdcd;
margin-bottom: 15px;
}
.plane > .win1 { background-color: #ddd; }
.plane > .win2 { background-color: #eee; }
.plane > .win3 { background-color: #fff; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="plane">
<div class="win1">Lorem ipsum</div>
<div class="win2">Dolor sit</div>
<div class="win3">amet.</div>
</div>
<div class="plane">
<div class="win1">Lorem ipsum</div>
<div class="win2">Dolor sit</div>
<div class="win3">amet.</div>
</div>

You cannot use + operator between a jQuery object and a string.
The correct way to do it is this:
$(".win1", onePlane).css("height", parseInt(baseHeight*1));
$(".win2", onePlane).css("height", parseInt(baseHeight*2));
$(".win3", onePlane).css("height", parseInt(baseHeight*3));
Each of these queries translates to: select all elements with .winX that are inside the jQuery object onePlane.

I would use all the same class names inside the nest and then just do $('.plane:eq(0) .win:eq(2)').html()
alert( $('.plane:eq(0) .win:eq(2)').html() );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="plane">
<div class="win">Lorem ipsum</div>
<div class="win">Dolor sit</div>
<div class="win">amet.</div>
</div>
<div class="plane">
<div class="win">Lorem ipsum</div>
<div class="win">Dolor sit</div>
<div class="win">amet.</div>
</div>

if your classes are fixed then you can do with this code
$(".win1", $(".plane")).css("height", parseInt(baseHeight*1));
$(" .win2", $(".plane")).css("height", parseInt(baseHeight*2));
$(" .win3", $(".plane")).css("height", parseInt(baseHeight*3));

You can do using each loop of plane class.
$('.plane').each(function(){
baseHeight = 10;
$(this).find(".win1").css("height", parseInt(baseHeight*1));
$(this).find(".win2").css("height", parseInt(baseHeight*2));
$(this).find(".win3").css("height", parseInt(baseHeight*3));
});
Demo

Related

How do I wrap adjacent elements of the same class using Javascript (no jQuery)

Everywhere I looked, it seemed that this problem has only been solved using jQuery, which I'm trying to remove completely from my project.
Here's the HTML:
<div class="codeblock"></div>
<div class="codeblock"></div>
<div class="codeblock"></div>
<p></p>
<div class="codeblock"></div>
<div class="codeblock"></div>
<p></p>
<div class="codeblock"></div>
desired result:
<div class="contentBox">
<div class="codeblock"></div>
<div class="codeblock"></div>
<div class="codeblock"></div>
</div>
<p></p>
<div class="contentBox">
<div class="codeblock"></div>
<div class="codeblock"></div>
</div>
<p></p>
<div class="contentBox">
<div class="codeblock"></div>
</div>
And here's how this can be done using jQuery, thanks to the many answers I've found on the topic
const e = '.codeblock';
$(e).not(e + '+' + e).each(function () {
$(this).nextUntil(':not(' + e + ')').addBack().wrapAll('<div class="contentBox" />');
});
Is there a way to replicate this same functionality using vanilla Javascript? I've tried using Element.nextElementSibling and checking if the class matches, but this approach wasn't very elegant and resulted in much more code than the jQuery solution.
Shortest version I could come up with:
let firstDivs = document.querySelectorAll('.codeblock:first-child, :not(.codeblock) + .codeblock');
firstDivs.forEach(function(div) {
let wrapper = document.createElement("div");
wrapper.className = 'wrapper';
div.parentNode.insertBefore(wrapper, div);
while(div.nextElementSibling && div.nextElementSibling.className == 'codeblock') {
wrapper.appendChild(div.nextElementSibling);
}
wrapper.insertBefore(div, wrapper.firstChild);
});
First, select the first .codeblock element out of each "group" - by selecting the element with that class that is the first child of its parent, and all those that do not have a .codeblock element before them.
For each of those elements, insert a new wrapper div before that element, then loop through the following element siblings, as long as they have that same class - and append those to the wrapper. And then afterwards, insert the first item to the beginning of the group. (If we did it before, the following elements would stop being siblings at this point.)
You could do something like this:
// Find all elements that match the class
document.querySelectorAll(`.${e}`).forEach(
// For each elemnt
elem => {
// If it's not the first of the group, skip it
if (elem.previousElementSibling!==null && elem.previousElementSibling.classList.contains(e)){
return;
}
// Find all adjacent elements with the same class
let o = [elem];
while (o[o.length - 1].nextElementSibling.classList.contains(e)) {
o.push(o[o.length - 1].nextElementSibling);
}
// Create a new wrapper element and give it a proper class
let wrapper = document.createElement('div');
wrapper.classList.add('contentBox');
// Insert the new wrapper immediatly before the group
elem.insertAdjacentElement('beforebegin', wrapper);
// Move the contents of the group to inside the wrapper element
wrapper.replaceChildren(...o);
}
)
It's a bit more code, but you can loop through all div and p, check every element and when matched append it to a new or existing div.codeBlock.
const isTargeted = el => el.classList.contains(`codeblock`);
const createWrap = (beforeEl) => beforeEl.insertAdjacentElement(`beforebegin`,
Object.assign(document.createElement(`div`), {className: `contentBox`}));
const divsAndPs = document.querySelectorAll(`div, p`);
divsAndPs.forEach(
(elem, i, self) => {
if (!i || isTargeted(elem)) {
const wrap = i && self[i-1].closest(`.contentBox`) ||
createWrap(elem);
wrap.appendChild(elem);
}
}
);
.contentBox {
color: green;
}
.contentBox .codeblock {
margin-left: 2rem;
}
.contentBox:before {
content: 'I am the great contentBox, here are my codeblocks:';
color: grey;
}
<div class="codeblock">x</div>
<div class="codeblock">x</div>
<div class="codeblock">x</div>
<p>paragraph</p>
<div class="codeblock">x</div>
<div class="codeblock">x</div>
<p>paragraph</p>
<div class="codeblock">x</div>
This can be a helper function (see also this stackblitz project):
const wrapIt = () => wrapAll(
document.querySelectorAll(`.codeblock, .codeblock + :not(.codeblock)`),
`codeblock`,
Object.assign(document.createElement(`div`), {className: `contentBox`}) );
setTimeout( wrapIt, 1000 );
function wrapAll(elems2Wrap, groupByClass, wrapperElement) {
const wrap = elem =>
elem.classList?.contains(groupByClass) && (elem
.previousElementSibling?.closest(`.${wrapperElement.className}`) ||
elem.insertAdjacentElement(`beforebegin`, wrapperElement.cloneNode())
).appendChild(elem);
elems2Wrap.forEach(wrap);
}
.contentBox {
color: green;
}
.contentBox .codeblock {
margin-left: 2rem;
}
.contentBox:before {
content: 'Wrapped!';
color: grey;
}
<div class="codeblock otherClass">x</div>
<div class="codeblock">x</div>
<div class="codeblock">x</div>
<p>Just a paragraph</p>
<div class="codeblock">x</div>
<div class="codeblock otherClass">x</div>
<p>Just a paragraph</p>
<div class="codeblock">x</div>

Jquery append while wrapping every x element

I'm trying to create HTML like this:
<div class="container">
<div class="wrap">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
<div class="wrap">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
<div class="wrap">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
</div>
The component used to add el element:
<input type="text" name="elements" />
el elements will appended to the container based on what number is added in the input. Every 3 elements should be wrapped in wrap div.
What I have so far:
$("input[name=elements]").on("keydown keyup", function() {
var amount = parseInt($(this).val());
for(i = 0; i < amount; i++) {
$(".container").append('<div class="el"></div>');
}
});
It adds the el divs but I'm not sure how to simultaneously wrap every 3 in wrap. Also is it possible to also remove el divs? If say I first type 8 in the input then I type 3, 11 divs will be added instead having just 3. In other words, the number of el divs in the HTML should alway be equal to the number in the input value. Would it make sense just to clear out the HTML first every time on input type?
You could first create an array of elements based on number of input value, append it to container and then wrap every nth element into wrap element.
const container = $('.container')
$("input").on('keyup', function() {
const val = parseInt($(this).val()) || 0;
const html = Array.from(Array(val), () => (
$("<div>", {
'class': 'el',
'text': 'element'
})
))
container.html(html)
for (let i = 0; i < val; i += 3) {
container
.find('.el')
.slice(i, i + 3)
.wrapAll("<div class='wrap'></div>");
}
})
.wrap {
border: 1px solid green;
margin: 10px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text">
<div class="container"></div>

Determine which div is in the middle of a div list

I have a div list that looks like this:
<div class="item"></div>
<div class="item"></div>
<div class="item"></div> <!--the middle one-->
<div class="item"></div>
<div class="item"></div>
I need to determine which div is in the middle of the list, please note that the div number is dynamic, depends on user's input. my final goal is to determine which divs are on the left and right side of the "middle div" then apply a class depends on its position.
The final result should look like this:
<div class="item left"></div>
<div class="item left"></div>
<div class="item center"></div> <!--the middle one-->
<div class="item right"></div>
<div class="item right"></div>
I was thinking to add a number identifier for each div and use median to determine the "middle div" but I'm not quite sure.
Perhaps there is a better approach for this problem using javascript, jquery or even pure css?
Update:
Additional information for handling even number:
in case the list has even number of child divs, it should divide it like this
<div class="item left"></div>
<div class="item left"></div>
<div class="item left"></div>
<div class="item right"></div>
<div class="item right"></div>
<div class="item right"></div>
in my problem, both Rory McCrossan and user3297291 works well. I added some modification to both of it for handling even numbers.
Rory McCrossan's (with JQuery):
var $items = $('.item');
var middleIndex = Math.floor($items.length / 2);
var hasMid = $items.length % 2;
console.log(middleIndex);
if(hasMid == 1){
$items.eq(middleIndex).addClass('middle')
.prevAll().addClass('left').end()
.nextAll().addClass('right');
}
if(hasMid == 0){
$items.eq(middleIndex).addClass('right')
.prevAll().addClass('left').end()
.nextAll().addClass('right');
}
user3297291's :
var setMidClasses = function (elementList, beforeMid, atMid, afterMid) {
var i = 0,
hasMid = elementList.length % 2,
mid = Math.floor(elementList.length / 2);
while (i < mid) {
elementList[i].classList.add(beforeMid);
i += 1;
}
if (hasMid == 1) {
elementList[i].classList.add(atMid);
i += 1;
}
while (i < elementList.length) {
elementList[i].classList.add(afterMid);
i += 1;
}
};
setMidClasses(document.querySelectorAll(".item"),
"left", "middle", "right");
feel free to edit the code snippets as it might be not very tidy after my edits.
In the case of an odd number of items you can get the middle item using Math.floor(items.length / 2). From there you can use prevAll() and nextAll() to add the classes to the relevant elements:
var $items = $('.item');
var middleIndex = Math.floor($items.length / 2);
$items.eq(middleIndex).addClass('center')
.prevAll().addClass('left').end()
.nextAll().addClass('right');
.left { color: red; }
.center { color: green; }
.right { color: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div> <!--the middle one-->
<div class="item">4</div>
<div class="item">5</div>
Without jQuery you might as well do like this;
var els = Array.from(document.querySelectorAll(".item")),
mid = ~~(els.length/2);
els.forEach((e,i) => i < mid ? e.classList.add("left")
: i === mid ? e.classList.add("center")
: e.classList.add("right"));
.left {color: red}
.center {color: green}
.right {color: blue}
<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>
Without jQuery:
(You haven't responded on how to handle even numbered lists. I've chosen to omit the center class and divide in to two parts: before and after mid)
var setMidClasses = function (elementList, beforeMid, atMid, afterMid) {
var i = 0,
hasMid = elementList.length % 2,
mid = Math.floor(elementList.length / 2);
while (i < mid) {
elementList[i].classList.add(beforeMid);
i += 1;
}
if (hasMid) {
elementList[i].classList.add(atMid);
i += 1;
}
while (i < elementList.length) {
elementList[i].classList.add(afterMid);
i += 1;
}
};
setMidClasses(document.querySelectorAll(".item"),
"top", "mid", "bottom");
.top { background-color: green; }
.mid { background-color: orange; }
.bottom { background-color: yellow; }
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div> <!--the middle one-->
<div class="item">4</div>
<div class="item">5</div>
Here's a version that uses recursion with .first()/.last()
probably not very efficient and could be done with a loop, but I wanted to show a version with recursion.
function fixthem()
{
var divs = $("div:not(.right):not(.left)");
// Handle evens, either 2 in the middle:
if (divs.length <= 2) return;
// or none in the middle
if (divs.length <= 1) return;
divs.first().addClass("left");
divs.last().addClass("right");
fixthem();
}
fixthem();
Here's the same without recursion and only a single jquery find at the start (ie hugely more efficient):
function fixthem()
{
var divs = $("div");
// Use 1 for 1 or none in the middle (when even), 2 for 1(odd) or 2(even)
while (divs.length > 2)
{
divs = divs.filter(":not(.right):not(.left)");
if (divs.length <= 2) break;
divs.first().addClass("left");
divs.last().addClass("right");
}
}
fixthem();
To add the class to the middle, run this after the function/at the end of the while loop:
$("div:not(.right):not(.left)").addClass("center")
Working fiddle: https://jsfiddle.net/5huLjh5q/
With center: https://jsfiddle.net/5huLjh5q/1/
var divs = $("div");
// Use 1 for 1 or none in the middle (when even), 2 for 1(odd) or 2(even)
while (divs.length > 2)
{
divs = divs.filter(":not(.right):not(.left)");
if (divs.length <= 2) break;
divs.first().addClass("left");
divs.last().addClass("right");
}
divs.addClass("center");
div { display:inline; border:1px solid black; padding: 1em; margin-top:0.5em }
.left { border:1px solid red;}
.right { border:1px solid green;}
.center { background: pink; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>

Add class to an element without an id

I have a list of items:
<div class="crew-item>
<div class="crew-grid"></div>
<div class="crew-detail></div>
</div>
<div class="crew-item>
<div class="crew-grid"></div>
<div class="crew-detail></div>
</div>
<div class="crew-item>
<div class="crew-grid"></div>
<div class="crew-detail></div>
</div>
When I click on a selected 'crew-grid' I'd like to add a class ('active') to its 'crew-item' parent, but I have no idea how to achieve that using vanilla js or jQuery.
The goal is to reveal the 'crew-detail' part, with active class added to its parent.
Like this?:
$('.crew-grid').on('click', function () {
$(this).closest('.crew-item').addClass('active');
});
Basically, starting from the clicked element, get the closest ancestor element which matches that selector. You don't need an id to target an element, just a way to identify it based on the information you have (in this case the clicked element).
If you want to de-activate other elements at the same time:
$('.crew-grid').on('click', function () {
$('.crew-item').removeClass('active');
$(this).closest('.crew-item').addClass('active');
});
Using jQuery :
$('.crew-grid').click(function() {
$(this).closest('.crew-item').addClass('active');
});
Use Document.querySelectorAll()
var crews = document.querySelectorAll('.crew-item');
if (crews) {
for (var i = 0; i < crews.length; i++) {
var grid = crews[i].querySelector('.crew-grid');
grid.addEventListener('click', toggleActive, false);
}
}
function toggleActive() {
var grids = document.querySelectorAll('.crew-item');
for (var i = 0; i < grids.length; i++) {
if (grids[i].classList.contains('active')) {
grids[i].classList.remove('active');
}
}
this.parentNode.classList.add('active');
}
.crew-item.active {
background: #DDD;
}
.crew-grid:hover {
cursor: pointer;
background: #eee;
}
<div class="crew-item active">
<div class="crew-grid">crew-grid</div>
<div class="crew-detail">crew-detail</div>
</div>
<br>
<div class="crew-item">
<div class="crew-grid">crew-grid</div>
<div class="crew-detail">crew-detail</div>
</div>
<br>
<div class="crew-item">
<div class="crew-grid">crew-grid</div>
<div class="crew-detail">crew-detail</div>
</div>

get element by id - using variable

I'm trying to use the function getElementById().
I have several ID's that i want to change style of, one by one, in an increasing order. I have a variable called numberOfTimes, which increases with 1 every time.
The names of the ID's are 1, 2, 3, 4 etc, and up to 8.
Is there some way I can use the variable-name in the function - if not, how should you solve this?
var numberOfTimes = 1;
document.getElementById(1).style.backgroundColor = "green";
Let's pretend your IDs look like this idx (x stands for the number). You can do:
var numberOfTimes = 8;
for (var i = 1; i <= numberOfTimes; ++i) {
document.getElementById('id' + i).style.backgroundColor = "green";
}
Solution if you really specific to change the color of the element one by one. Then you can use for loop.
second
document.getElementById(1)
You can use integer value directly but preferable you should use id with some text.
just use like this in for loop
for(var i=1;i<=8;i++){
document.getElementById("div_"+i).style.backgroundColor = "green";
}
As stated in other answers and comments, it is highly recommended not to use numbers only as IDs, also you can pick these divs upon a unique class name which will get you an array of them, and just loop through the array just like in this fiddle.
However here's the code JS Fiddle
var id = 1;
changeBG();
function changeBG() {
document.getElementById('el-' + id).style.backgroundColor = "green";
id++;
var t = setTimeout(changeBG, 500);
}
div {
width: 100%;
height: 50px;
margin-bottom: 5px;
display: inline-block;
}
<div id="el-1"></div>
<div id="el-2"></div>
<div id="el-3"></div>
<div id="el-4"></div>
<div id="el-5"></div>
<div id="el-6"></div>
<div id="el-7"></div>
<div id="el-8"></div>
Numbers-only ID are not a good practice, I hope this quetion is in the purpose of learning.
for(var i = 1; i < 9; i++){
document.getElementById(i).style['background'] = 'green';
}
div {
width:100%;
height:20px;
margin-bottom:10px;
}
<div id="1"></div>
<div id="2"></div>
<div id="3"></div>
<div id="4"></div>
<div id="5"></div>
<div id="6"></div>
<div id="7"></div>
<div id="8"></div>

Categories

Resources