Modify DOM based on amount of divs after specific class - javascript

Is there any way to modify DOM based on amount div after specific class?
For example, if I have a div with a class called row and after that I have 4 div elements. Is there a way to change these 4 div element class depending on how many div elements there are?
Code:
<div class="row">
<div class="col-1-of-4">
some content
</div>
<div class="col-1-of-4">
some content
</div>
<div class="col-1-of-4">
some content
</div>
<div class="col-1-of-4">
some content
</div>
</div>
Another example I have a div class row again, but this time I want 3 div elements after that, then I would want these div elements to have a class called col-1-of-3, not col-1-of-4. If I would have just 2 div elements after that then class col-1-of-2 and if just one div element then no class at all.:
Code:
<div class="row">
<div class="col-1-of-3">
some content
</div>
<div class="col-1-of-3">
some content
</div>
<div class="col-1-of-3">
some content
</div>
</div>
Also these div elements with classes called col-1-of-4, col-1-of-3 and col-1-of-2 have their own div elements inside them, but they should stay like they were.
Is it possible to achieve with JavaScript or PHP?

You would need to write conditional blocks to handle this if I'm understanding you correctly (wanting a JS or PHP solution).
Note: It goes without saying that a similar solution can be completed with a CSS-only approach, as outlined here: Can CSS detect the number of children an element has?
Here's an example (using jQuery) with 3 sets of row's, with varying children (2, 3, 4):
$(function() {
var $rows = $(".row");
$rows.each(function() {
$row = $(this);
var $children = $(">div", $row),
total = $children.size();
$children.addClass("col-1-of-" + total);
});
});
.row {
border: 1px solid #000;
margin: 10px;
}
.row > div {
margin: 10px;
}
.row .col-1-of-2 {
border: 1px solid #f00;
}
.row .col-1-of-3 {
border: 1px solid #0f0;
}
.row .col-1-of-4 {
border: 1px solid #00f;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row">
<div>
some content
</div>
<div>
some content
</div>
</div>
<div class="row">
<div>
some content
</div>
<div>
some content
</div>
<div>
some content
</div>
</div>
<div class="row">
<div>
some content
</div>
<div>
some content
</div>
<div>
some content
</div>
<div>
some content
</div>
</div>
When you run the snippet, you must inspect the elements. I've added borders so you can see the difference.

Theres a number of ways to achieve this. I'd maybe add another class name so you can easily identify groups of divs, and differentiate between parent and child divs. Does this help you get where you're going? Basically find the number of children in a row and then concatenate that number into the class name.
var x = document.getElementsByClassName('row')[0].childElementCount
var element = document.getElementsByClassName('row')[0];
element.classList.add(`col-1-of-${x}`);

.row {
width: 100%;
display:flex;
flex-grow: 1;
margin-bottom: 2px;
}
.col {
float:left;
background: rgba(255,0,0,.2);
text-align: center;
margin-right: 2px;
}
.col:first-child:nth-last-child(1),
.col:first-child:nth-last-child(1) ~ .col{
width: calc(100% / 1);
}
.col:first-child:nth-last-child(2),
.col:first-child:nth-last-child(2) ~ .col{
width: calc(100% / 2);
}
.col:first-child:nth-last-child(3),
.col:first-child:nth-last-child(3) ~ .col{
width: calc(100% / 3);
}
.col:first-child:nth-last-child(4),
.col:first-child:nth-last-child(4) ~ .col{
width: calc(100% / 4);
}
.col:first-child:nth-last-child(5),
.col:first-child:nth-last-child(5) ~ .col{
width: calc(100% / 5);
}
<div class="row">
<div class="col">1</div>
</div>
<div class="row">
<div class="col">1</div>
<div class="col">2</div>
</div>
<div class="row">
<div class="col">1</div>
<div class="col">2</div>
<div class="col">3</div>
</div>
<div class="row">
<div class="col">1</div>
<div class="col">2</div>
<div class="col">3</div>
<div class="col">4</div>
</div>
<div class="row">
<div class="col">1</div>
<div class="col">2</div>
<div class="col">3</div>
<div class="col">4</div>
<div class="col">5</div>
</div>
so this is with float, can be used in a sass/scss mixin to create code automagically. there should be also a flex solution but i dont have it at hand at the moment

Related

Javascript : finding a specific previous element on list and adding class

I have a list like this.
Inside each .list item there is a html button :
<div class="list">
<button>.list</button>
</div>
Also, each item can be inside a .bloc element
<div class="list"><button>.list</button></div>
<div class=bloc>
<div class="list"><button>.list</button></div>
</div>
When I click on the button, I would like the previous .list item to have the .active class like so :
Well it’s pretty easy with jquery and i've done that, it’s work pretty well :
$('.list button').on('click', function() {
$(this).closest('.list').prev('.list').addClass('active');
});
BUT i have some specific cases :
Sometimes the list items can be hidden and a list with hidden class can’t have .active class :
Or more complicated. You have to go up on each item one by one and put the active class to the first which does not have the hidden class :
I did the mechanics for items without class hidden, but I'm afraid I'm going in the wrong direction because the number of cases is getting bigger and bigger. Ain't there a smarter way ? :o
$('.list button').on('click', function() {
if ($(this).closest('.list').prev().length === 0) {
if ($(this).closest('.bloc').length) {
$(this).closest('.bloc').prev('.list').addClass('active');
$(this).closest('.bloc').prev('.bloc').find('.list:last-child').addClass('active');
} else {
$(this).closest('.list').next('.list').addClass('active');
}
}
if ($(this).closest('.list').prev('.bloc').length) {
$(this).closest('.list').prev('.bloc').find('.list:last-child').addClass('active');
}
$(this).closest('.list').prev('.list').addClass('active');
}
Rather than use .closest .prev and .next you can use the overload to .index which will give you the index within an existing collection.
var idx = collection.index(element);
select all your .list items into a jquery object/collection
when clicking get the index within that collection
subtract 1 to get the previous .list item within that collection
The basic scenarios are covered with $(".list") :
// collate the list first
var list = $(".list");
// add click handler
list.click(function() {
// confirm there are no duplicates
// comapred with $(this).index() which is the index within the parent
console.log(list.index(this), $(this).index())
$(".active").removeClass("active");
var idx = list.index(this);
if (idx > 0)
list.eq(idx-1).addClass("active");
});
.list { border:1px solid #CCC; height: 20px; }
.bloc { border:1px solid #444; padding: 5px; }
.active { border:1px solid red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='wrapper'>
<div class='bloc'>
<div class='list'></div>
<div class='list'></div>
</div>
<div class='list'></div>
<div class='list'></div>
</div>
All the other use-cases are then just a case of providing the correct selector up-front, with otherwise exactly the same code
var list = $(".wrapper>.bloc:not(.hidden)>.list:not(.hidden),.wrapper>.list:not(.hidden)");
I've tried to recreate some of your scenarios, but if there's one that's missing, please comment and I'll ensure it fits (within the remit of the question).
Giving:
var list = $(".wrapper>.bloc:not(.hidden)>.list:not(.hidden),.wrapper>.list:not(.hidden)")
list.click(function() {
$(".active").removeClass("active");
var idx = list.index(this);
if (idx > 0)
list.eq(idx-1).addClass("active");
});
.list { border:1px solid #CCC; height: 20px; }
.bloc { border:1px solid #444; padding: 5px; }
.active { border:1px solid red; }
.hidden { background-color: #ccc; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='wrapper'>
<div class='bloc'>
<div class='list'></div>
<div class='list'></div>
</div>
<div class='list hidden'></div>
<div class='bloc'>
<div class='list hidden'></div>
<div class='list hidden'></div>
</div>
<div class='list'></div>
<div class='bloc'>
<div class='list hidden'></div>
<div class='list'></div>
</div>
<div class='list'></div>
<div class='list'></div>
<div class='bloc hidden'>
<div class='list'></div>
<div class='list'></div>
</div>
<div class='list'></div>
<div class='list'></div>
</div>

Dynamically change nth-child number

ngx-datatable > div > datatable-body > datatable-selection:hover > datatable-scroller > datatable-row-wrapper:nth-child(n) > datatable-body-row > div.datatable-row-center.datatable-row-group > datatable-body-cell:nth-child(1) {
background-color: #E9F1FA !important;
}
This is my long selector. I am trying to highlight the entire column based on the user hovering over an item on a column. This works great, except that it only highlights whatever nth-child I am using on the last datatable-body-cell:nth-child(1) I can change it to any number and it works, but it isn't dynamic. I want it to only select the column that is being hovered over. I've tried datatable-body-cell:nth-child(n):hover and datatable-body-cell:hover and a lot of different varieties but it either highlights the whole table, or nothing at all, unless I specify the nth-child.
Is there a way I can dynamically change the nth-child based on the what child the user is hovering over (with CSS or Javascript)?
Any help would be appreciated!
You can use document.querySelector to get the column and set its style it is being hovered over on mouseenter and reset it back to normal on mouseleave.
var n = 1;//the number
document.querySelector('ngx-datatable > div > datatable-body > datatable-selection:hover > datatable-scroller > datatable-row-wrapper:nth-child('+n+') > datatable-body-row > div.datatable-row-center.datatable-row-group > datatable-body-cell:nth-child(1)').style.setProperty('background-color', '#E9F1FA', 'important');
Demo:
var children = document.querySelectorAll('div.child');
Array.prototype.slice.call(children).forEach(function(child){
var n = child.parentNode.getAttribute('data-num');
var parent = document.querySelector('div.table>div:nth-child('+n+')');
child.addEventListener('mouseenter', function(e){
parent.style.backgroundColor = "yellow";
this.style.backgroundColor = "red";
});
child.addEventListener('mouseleave', function(e){
var n = +this.parentNode.getAttribute('data-num');
parent.style.backgroundColor = "";
this.style.backgroundColor = "";
});
});
.table{
height: 250px;
width: 400px;
margin: 5px;
padding: 5px;
background-color: goldenrod;
}
.column{
background-color: dodgerblue;
margin: 5px;
}
.child{
border: 1px solid black;
}
<div class="table">
<div class="column" data-num="1">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
</div>
<div class="column" data-num="2">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
</div>
<div class="column" data-num="3">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
</div>
</div>

css selector to exclude certain descendants and their descendants of jquery object?

Given a jquery object that has many levels of descendants, how do you exclude certain descendants AND their descendants? Assume there is a class (.foo in this case) on the top node of the elements intended for exclusion and your jquery object is the div1 below.
EDIT: Clarification: I want to exclude ALL descendants of .foo and not just the immediate children.
$('#button1').click(function() {
var selector = '#test ' + $('#input1').val();
var numberElements = $(selector).length;
$('#numberElements').text(numberElements);
var elementsAsCommaSeparatedList = _.pluck($(selector), 'id');
$('#elementList').text(elementsAsCommaSeparatedList.join(', '));
});
#input1 {
width: 230px;
font-size: 16px;
font-weight: bold;
}
#numberElements,
#elementList {
color: green;
font-weight: bold;
font-size: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<label for="input1">Please supply a selector... $('<input id="input1" value="div:not(.foo, .foo *)"></label>')
<button id="button1">Apply Selector</button>
<div>(default value is from #DaniP's answer)</div>
<div id="result"><span>Number of elements selected: </span><span id="numberElements">0</span></div>
<div>
<span>Elements selected (by id):</span>
<span id="elementList"></span>
</div>
<div id="test">
<div id="div1">
<div id="div2">
<div id="div4">
<div id="div8"></div>
<div id="div9"></div>
</div>
<div id="div5" class="foo">
<div id="div10"></div>
<div id="div11"></div>
</div>
</div>
<div id="div3">
<div id="div6">
<div id="div12"></div>
<div id="div13"></div>
</div>
<div id="div7" class="foo">
<div id="div14"></div>
<div id="div15"></div>
</div>
</div>
</div>
</div>
JQuery
With Jquery you can use more than one argument on the :not selector this way will be easy to exlude the .foo and child elements:
$('div:not(.foo, .foo *)')
Example Snippet
$('div:not(.foo, .foo *)').css('border-color','blue')
div {
padding-left:20px;
margin:5px;
border:thin red solid;
}
.foo {
background:rgba(0,0,0,.1)
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
DIV1
<div>
DIV2
<div>
DIV4
<div>DIV8</div>
<div>DIV9</div>
</div>
<div class="foo">
DIV5
<div>DIV10</div>
<div>DIV11</div>
</div>
</div>
<div>
DIV3
<div>
DIV6
<div>DIV12</div>
<div>DIV13</div>
</div>
<div class="foo">
DIV7
<div>DIV14</div>
<div>DIV15</div>
</div>
</div>
</div>
But CSS doesn't allow that, refer to this answer.
CSS
On CSS you will need to target all divs that aren't .foo but also overwrite again the child elements of .foo:
div:not(.foo) {
border-color:blue;
}
div.foo * {
border-color:red;
}
Example Snippet
div {
padding-left:20px;
margin:5px;
border:thin red solid;
}
.foo {
background:rgba(0,0,0,.1)
}
div:not(.foo) {
border-color:blue;
}
div.foo * {
border-color:red;
}
<div>
DIV1
<div>
DIV2
<div>
DIV4
<div>DIV8</div>
<div>DIV9</div>
</div>
<div class="foo">
DIV5
<div>DIV10</div>
<div>DIV11</div>
</div>
</div>
<div>
DIV3
<div>
DIV6
<div>DIV12</div>
<div>DIV13</div>
</div>
<div class="foo">
DIV7
<div>DIV14</div>
<div>DIV15</div>
</div>
</div>
</div>
Maybe use the :not selector ?
Something like
div:not(.foo)

Getting divs next to each other when clicking on a button / JQuery

i am making a kind of storyboard where you can add and remove frames but i need to set divs next to each other, the code i now have it places the div's beneath each other. I want to make it with a loop
Here is my code:
HTML
<div id="storyboard">
<div id="container">
<div class="frame">
<div class="frame__outer">
<div class="frame__inner"></div>
<div class="frame__content"></div>
<div type="button" value="fade_in" class="add__button"> + </div>
</div>
</div>
</div>
</div>
JS
_this.addClickFunction = function() {
var i = 0;
$('.add__button').click(function() {
$('.frame').after('<div id="container'+(i++)+'"></div> <div class="frame__outer"> <div class="frame__inner"></div><div class="frame__content"></div></div>');
});
};
Use append() instead of after() function. This should work:
_this.addClickFunction = function() {
var i = 0;
$('.add__button').click(function() {
$('.frame').append('<div id="container'+(i++)+'"></div> <div class="frame__outer"> <div class="frame__inner"></div><div class="frame__content"></div></div>');
});
};
This works for keeping one .frame element and adding multiple divs to it of the structure:
<div class="container[i]">
<div class="frame__outer">
<div class="frame__inner"></div>
<div class="frame__content"></div>
</div>
</div>
If you want to arrange elements side by side which normaly are block elements and thus are positioned underneath eachother by default use either css floats or css flexbox.
https://css-tricks.com/all-about-floats/
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
i need to set divs next to each other
Try this example to add new story container to all current .container
var i = 1;
$('.add__button').click(function() {
i++;
$(".container").each(function(x) {
$(this).after('<div id="container' + x + '_' + i + '" class="container"><div class="frame"><div class="frame__outer"> <div class="frame__inner"></div><div class="frame__content">story ' + i + '</div></div></div></div>');
});
});
.frame__outer {
padding: 20px;
background: #222;
color: white;
border-bottom: solid 3px green;
margin: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="storyboard">
<input type='button' value='add story' class="add__button" />
<div id="container" class='container'>
<div class="frame">
<div class="frame__outer">
<div class="frame__inner"></div>
<div class="frame__content">story 1</div>
</div>
</div>
</div>
</div>

Apply colored circle on top of the clicked image

I have a page that when loaded displays this:
The HTML for this is as follows (the below is built within a foreach statement in the view as I'm using MVC 5)
<div class="boxTop"></div>
<div id="panel1" class="box">
<div class="row col-xs-12 margin0" style="margin-left:-8%">
<div class="col-md-6 col-xs-6">
<img data-name="blackcherry" alt="cherries.png" data-id="1" src="/Content/Images/FlavourLab/cherries.png">
</div>
<div class="col-md-6 col-xs-6">
<img data-name="coconut" alt="coconut" data-id="2" src="/Content/Images/FlavourLab/coconut.png">
</div>
</div>
<div class="clearfix"></div>
<div class="marginBottom10 visible-xs-block"></div>
<div class="row col-xs-12 margin0" style="margin-left:-8%">
<div class="col-md-6 col-xs-6">
<img data-name="mango" alt="mango" data-id="3" src="/Content/Images/FlavourLab/mango.png">
</div>
<div class="col-md-6 col-xs-6">
<img data-name="strawberries" alt="strawberries" data-id="4" src="/Content/Images/FlavourLab/strawberries.png">
</div>
</div>
<div class="clearfix"></div>
<div class="marginBottom10 visible-xs-block"></div>
</div>
<div class="boxBtm"></div>
What I'm trying to do is when one of those images are clicked I need to place the following css circle on top of it to show its been selected the CSS for the circle is like this
#circle1 {
background: none repeat scroll 0 0 green;
height: 80px;
width: 80px;
opacity: 0.4;
}
.circle {
border-radius: 50%;
display: inline-block;
margin-right: 20px;
}
Which gets rendered like this:
<div class="circle" id="circle"></div>
My current jQuery is like this:
$("#panel1 row img").click(function () {
var id = $(this).attr("data-id").val();
alert(id);
});
2 Things:
The jQuery does not fire, I'm unsure why. Can someone explain this?
How would I add the above CSS Circle to the clicked image?
This #panel1 row img is a wrong selector, change it to #panel1 .row img - note class name selector .row
Change your click handler to do this $(this).toggleClass("circle");
.circle class shall look like:
.circle {
border-radius: 50%;
border: 2px solid red;
overflow: visible;
}
Try something like this (the "row" class missed the dot in the selector)
$("#panel1 .row img").click(function () {
$(this).addClass('circle');
});

Categories