Code only working in Firefox - javascript

The given code is only working in Firefox and not in any other browser.
I have given the Fiddle link for it.The code is working fine in Firefox and the functions are also working but it is not supported by any other browser.
The error shows is
Error due to long Script
Fiddle
Here is the code.
var $boxes;
$(document).ready(function() {
$boxes = $(".box");
setupColumns();
$(window).on("resize", setupColumns);
});
function setupColumns() {
var $columnwrapper = $("#columns");
var w = $("<div>").addClass("column").width();
var cnt = Math.floor($columnwrapper.width() / w);
var cols = [];
for (var i = 0; i < cnt; i++) {
var $col = $("<div>").addClass("column");
cols.push($col);
}
$columnwrapper.append(cols);
var cnt = 0;
$boxes.each(function() {
$(this).detach().appendTo(cols[cnt]);
cnt = (cnt + 1) % cols.length;
});
}
$(".box").click(function() {
if ($(this).height() != 100)
$(this).animate({
height: 100
}, 1000);
else
$(this).animate({
height: 150
}, 1000);
});
.column {
width: 114px;
float: left
}
.box {
height: 100px;
width: 100px;
border: 2px solid;
margin-bottom: 10px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="columns"></div>
<div class="box">HELLO WORLD 1</div>
<div class="box">HELLO WORLD 2</div>
<div class="box">HELLO WORLD 3</div>
<div class="box">HELLO WORLD 4</div>
<div class="box">HELLO WORLD 5</div>
<div class="box">HELLO WORLD 6</div>
<div class="box">HELLO WORLD 7</div>
<div class="box">HELLO WORLD 8</div>
<div class="box">HELLO WORLD 9</div>
<div class="box">HELLO WORLD 10</div>

The problem is due to following line:
var w = $("<div>").addClass("column").width();
Apart from Firefox, in other browsers its returning 0 which causes cnt to become Infinity. That's why you are getting a really long running script that's creating infinite divs and adding them to cols[]
Documentation for .width() says:
The value reported by .width() is not guaranteed to be accurate when
the element or its parent is hidden. To get an accurate value, ensure
the element is visible before using .width().
So what you will need to do is:
var $boxes;
$(document).ready(function() {
$boxes = $(".box");
setupColumns();
$(window).on("resize", setupColumns);
});
function setupColumns() {
var $columnwrapper = $("#columns");
//////////start change////////////
var dummy = $("<div>").addClass("column");
dummy.appendTo($columnwrapper); // add it to wrapper so that it gets displayed
var w = dummy.width(); // this now returns 114
dummy.remove(); // now that we have got the width, remove it
// just to be on safer side:
if(w == 0){
console.log("column width is 0.");
return;
}
//////////end change////////////
var cnt = Math.floor($columnwrapper.width() / w);
var cols = [];
for (var i = 0; i < cnt; i++) {
var $col = $("<div>").addClass("column");
cols.push($col);
}
$columnwrapper.append(cols);
var cnt = 0;
$boxes.each(function() {
$(this).detach().appendTo(cols[cnt]);
cnt = (cnt + 1) % cols.length;
});
}
$(".box").click(function() {
if ($(this).height() != 100)
$(this).animate({
height: 100
}, 1000);
else
$(this).animate({
height: 150
}, 1000);
});
.column {
width: 114px;
float: left
}
.box {
height: 100px;
width: 100px;
border: 2px solid;
margin-bottom: 10px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="columns"></div>
<div class="box">HELLO WORLD 1</div>
<div class="box">HELLO WORLD 2</div>
<div class="box">HELLO WORLD 3</div>
<div class="box">HELLO WORLD 4</div>
<div class="box">HELLO WORLD 5</div>
<div class="box">HELLO WORLD 6</div>
<div class="box">HELLO WORLD 7</div>
<div class="box">HELLO WORLD 8</div>
<div class="box">HELLO WORLD 9</div>
<div class="box">HELLO WORLD 10</div>

This is maybe because of cross-browser security concept by the modern browsers. If you are trying to run in Google Chrome. You might have to use the older version or install the plugin such as this:
https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en

Related

Best way to position divs in horizontal timeline taking into account free space / overlaps

I'm trying to create a horizontal timeline but I'm not sure the best way to go about arranging the events divs correctly. At the moment it looks like this:
<div id="events">
<div class="event" style="left:25; position:relative;" id="1">• Entry 1</div>
<div class="event" style="left:25; position:relative;" id="2">• Entry 2</div>
<div class="event" style="left:50; position:relative;" id="3">• Entry 3</div>
<div class="event" style="left:375; position:relative;" id="4">• Entry 4</div>
</div>
I'm trying to make Entry 4 position itself at the top (as there are no divs in the way) but I'm not sure the best solution. It also needs to allow for any number of events overlapping.
Most css options / jQuery plugins don't seem to offer a solution as far as I'm aware they are mostly for flexible grids but this only needs to be flexible vertically and have fixed positions horizontally to line up correctly with the dates.
An obvious first step is to position: absolute and set a top: x but how would one go about checking previous entries to make sure it's not overlapping an older & longer entry. The timeline will hold quite a number of events with various lengths so it can't be too intensive either.
Any suggestions for the best/easiest way to do this?
I suppose you have an events array with start and end dates, then you should check whether events are overlapping by start and end dates. To simulate this, you can check this method:
var events = [{
start: "2018/10/24 15:00",
end: "2018/10/24 18:00"
}, {
start: "2018/10/25 12:00",
end: "2018/10/26 12:00"
}, {
start: "2018/10/25 07:00",
end: "2018/10/25 10:00"
}, {
start: "2018/10/24 12:00",
end: "2018/10/24 20:00"
}, {
start: "2018/10/25 08:00",
end: "2018/10/25 13:00"
}];
var stack = [],
s = 0,
lastStartDate, lastEndDate, newEvents;
events.sort(function(a,b){
if(a.start > b.start) return 1;
if(a.start < b.start) return -1;
return 0;
});
while (events.length > 0) {
stack[s] = [];
newEvents = [];
stack[s].push(events[0]);
lastStartDate = events[0].start;
lastEndDate = events[0].end;
for (var i = 1; i < events.length; i++) {
if (events[i].end < lastStartDate) {
stack[s].push(events[i]);
lastStartDate = events[i].start;
delete events[i];
} else if (events[i].start > lastEndDate) {
stack[s].push(events[i]);
lastEndDate = events[i].end;
}else{
newEvents.push(events[i]);
}
}
events = newEvents;
s++;
}
console.log(stack);
This method picks the first event as the key and checks for other events whether they overlap or not, if they don't overlap, they will be added to the first stack, then if there are any events left, a new stack will be created and then with the remaining events, the code will be run again and a new stack will be prepared again. Until there are any events left in the array, it should continue creating stacks.
Then you can use these stacks to build your events grid. One stack per line.
I used this algorithm in my Evendar plugin. You can check it's grid view.
grid display has a mode, auto-flow : dense, that can do what you want.
In the following example, I have divided every visible column in 4 subcolumns, to allow to set positions in quartes of an hour .
Then, I have classes to set the beginning and end of the events. Of course, you could inject the styles inline and get the same result:
.grid {
margin: 10px;
width: 525px;
border: solid 1px black;
display: grid;
grid-template-columns: repeat(24, 20px);
grid-auto-flow: dense;
grid-gap: 2px;
}
.head {
grid-column: span 4;
background-color: lightblue;
border: solid 1px blue;
}
.elem {
background-color: lightgreen;
}
.start1 {
grid-column-start: 1;
}
.start2 {
grid-column-start: 2;
}
.start3 {
grid-column-start: 3;
}
.start4 {
grid-column-start: 4;
}
.start5 {
grid-column-start: 5;
}
.start6 {
grid-column-start: 6;
}
.end2 {
grid-column-end: 3;
}
.end3 {
grid-column-end: 4;
}
.end4 {
grid-column-end: 5;
}
.end5 {
grid-column-end: 6;
}
.end6 {
grid-column-end: 7;
}
<div class="grid">
<div class="head">1</div>
<div class="head">2</div>
<div class="head">3</div>
<div class="head">4</div>
<div class="head">5</div>
<div class="head">6</div>
<div class="elem start1 end2">A</div>
<div class="elem start2 end5">B</div>
<div class="elem start2 end3">C</div>
<div class="elem start3 end4">D</div>
<div class="elem start3 end3">E</div>
<div class="elem start5 end6">F</div>
<div class="elem start5 end7">G</div>
<div class="elem start6 end8">H</div>
</div>
UPDATE
In addition to inline style, You can also rely on CSS variables in order to easily manage the grid with less of code:
.grid {
margin: 10px;
width: 525px;
border: solid 1px black;
display: grid;
grid-template-columns: repeat(24, 20px);
grid-auto-flow: dense;
grid-gap: 2px;
}
.head {
grid-column: span 4;
background-color: lightblue;
border: solid 1px blue;
}
.elem {
background-color: lightgreen;
grid-column-start: var(--s, 1);
grid-column-end: var(--e, 1);
}
<div class="grid">
<div class="head">1</div>
<div class="head">2</div>
<div class="head">3</div>
<div class="head">4</div>
<div class="head">5</div>
<div class="head">6</div>
<div class="elem" style="--s:1;--e:3">A</div>
<div class="elem" style="--s:5;--e:6">B</div>
<div class="elem" style="--s:1;--e:2">C</div>
<div class="elem" style="--s:3;--e:5">D</div>
<div class="elem" style="--s:1;--e:8">E</div>
<div class="elem" style="--s:3;--e:4">F</div>
<div class="elem" style="--s:4;--e:6">G</div>
<div class="elem" style="--s:2;--e:3">H</div>
</div>
I think this cannot be done with only CSS, so we need to rely on some JS/jQuery.
My idea is to only set the left property within the element, then I loop through all the element to define the top value. I start by setting top to 0 and check if there is already an element in that position.
If not: I place it there and move to next element.
if yes: I increase the top value and check again. I continue until I find a place.
Here a simplified code where I set a random value to left and apply the logic described above.
$('.event').each(function(){
var top=0;
var left = Math.floor(Math.random() *(400));
/* we need to test between left and left+ width of element*/
var e1 = document.elementFromPoint(left, top);
var e2 = document.elementFromPoint(left+80, top);
/* we check only placed element to avoid cycle*/
while ((e1 && e1.classList.contains('placed')) || (e2 && e2.classList.contains('placed'))) {
top += 20; /*Height of an element*/
e1 = document.elementFromPoint(left, top)
e2 = document.elementFromPoint(left+80, top)
}
$(this).css({top:top, // we set the top value
left:left, // we set the left value
zIndex:3// we increase z-index because elementFromPoint consider the topmost element
});
$(this).addClass('placed'); //we mark the element as placed
});
* {
box-sizing: border-box;
}
body {
margin:0;
}
#events {
height: 300px;
width:calc(80px * 6 + 2px);
background:repeating-linear-gradient(to right,blue 0,blue 2px,transparent 2px,transparent 80px);
position:relative;
}
.event {
position: absolute;
width: 80px;
height: 20px;
border: 1px solid #000;
z-index:2;
background:red;
color:#fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="events">
<div class="event" >• Entry 1</div>
<div class="event" >• Entry 2</div>
<div class="event" >• Entry 3</div>
<div class="event" >• Entry 4</div>
<div class="event" >• Entry 5</div>
<div class="event" >• Entry 6</div>
<div class="event" >• Entry 7</div>
<div class="event" >• Entry 8</div>
</div>
You can get something of that effect with just display: grid. Though you will need to be mindful of the order you create the divs.
var grid = document.getElementById("grid")
for (let i = 0; i <= 9; i++)
{
let div = document.createElement("div");
div.innerHTML = i
grid.append(div);
}
let div
div = document.createElement("div");
div.innerHTML = "Entry 1";
div.style.gridColumn = 1
grid.append(div)
div = document.createElement("div");
div.innerHTML = "Entry 4";
div.style.gridColumn = 10
grid.append(div)
div = document.createElement("div");
div.innerHTML = "Entry 2";
div.style.gridColumn = 1
grid.append(div)
div = document.createElement("div");
div.innerHTML = "Entry 3";
div.style.gridColumn = 2
grid.append(div)
#grid {
display: grid;
grid-template-columns: repeat(10, 1fr);
}
<div id="grid" >
</div>
Next is an example of how I would go about it, I have populated events' starting/ending points with random values along a 12h time line. Since the values are random, they might be a little rough around the edges sometimes ;).
Also I assumed two events, A and B, can have the same top if, for instance, A ends at exactly the same time as B or vice versa.
I have commented the code as much as I thought necessary to explain the proceedings.
Hope it helps
$(document).ready(function() {
var timeline = $('#time-line');
for (var i = 0; i < 12; i++)
timeline.append('<div>' + (i + 1) + '</div>')
/*to check event overlapping later*/
var eventData = [];
/*generate random amount of events*/
var eventCount = Math.random() * 10 + 1;
var eventsContainer = $('#events');
var total = 720; //12h * 60min in the example
for (var i = 0; i < eventCount; i++) {
var start = Math.floor(Math.random() * total);
var end = Math.floor(Math.random() * (total - start)) + start;
var duration = end - start;
var event = $('<div class="event" id="' + i + '" >E' + i + '(' + duration + ' min.' + ')</div>');
event.attr('title', 'Start: ' + Math.floor(start / 60) + ':' + (start % 60) + ' | '
+ 'End: ' + Math.floor(end / 60) + ':' + (end % 60));
event.css('width', (duration * 100 / 720) + "%");
event.css('left', (start * 100 / 720) + "%");
var top = getTop(start, end);
event.css('top', top);
/*store this event's data to use it to set next events' top property in getTop()*/
eventsContainer.append(event);
eventData.push([start, end, top, event.height() + 1]); //the +1 is to compensate for the 1px-wide border
}
/**
* Get the event's top property by going through the previous events and
* getting the 'lowest' yet
*
* #param {Number} start
* #param {Number} end
* #returns {Number}
*/
function getTop(start, end) {
var top = 0;
/*for each previous event check for vertical collisions with current event*/
$.each(eventData, function(i, data /*[start, end, top]*/) {
if (data[2] + data[3] > top //if it's height + top is not the largest yet, skip it
//if any of the next 6 conditions is met, we have a vertical collision
//feel free to optimize but tread carefully
&& (data[0] >= start && data[0] < end
|| data[0] < start && data[1] >= end
|| data[0] < start && data[1] > start
|| data[0] < end && data[1] >= end
|| data[0] >= start && data[1] < end
|| data[0] <= start && data[1] > start)) {
top = data[2] + data[3];
}
});
return top;
}
});
#time-line {
width: 100%;
text-align: center
}
#time-line div {
display: inline-block;
width: calc(100% / 12); //12h for the example
text-align: right;
background: lightgray;
}
#time-line div:nth-child(odd) {
background: gray
}
#events {
position: relative
}
#events .event{
position: absolute;
background: lightblue;
text-align: center;
border: 1px solid black
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="time-line"></div>
<div id="events"></div>
Here's a relatively simple function which can be applied to the HTML markup you have. I have hardcoded the height (28px) but the width of the event elements can be variable. The end result is something like this:
A few requirements for this solution to work:
The left property needs to be defined in the HTML markup of each event (as in your example), like:
<div class="event" style="left:25px; position:relative;" id="1">• Entry 1</div>
The events need to be in order, with each event element's left property equal to, or greater, than the previous element's left property
arrangeEvents()
Accepts a selector for the event elements as an argument, then loops through them and applies a top property as needed depending on the end of the longest previous event and start of the current event.
In short, if the start time of the current event is less than the end of a previous event, the current event is positioned below the previous events. If the start of the current event is greater than the end of the previous events, the current event is positioned at the top.
const arrangeEvents = (els) => {
let offset = 1;
let end = 0;
Array.from(els).forEach((event, index) => {
const posLeft = parseInt(event.style.left);
offset = posLeft >= end ? 0 : ++offset;
end = Math.max(end, posLeft + event.offsetWidth);
event.style.top = `-${28 * (index - offset)}px`;
});
}
arrangeEvents(document.querySelectorAll('.event'));
#events {
position: relative;
}
.event {
display: inline-block;
float: left;
clear: both;
border: 1px solid lightgray;
padding: 4px;
height: 28px;
box-sizing: border-box;
}
<div id="events">
<div class="event" style="left:25px; position:relative;" id="1">• Entry 1</div>
<div class="event" style="left:25px; position:relative;" id="2">• Entry 2</div>
<div class="event" style="left:50px; position:relative;" id="3">• #3</div>
<div class="event" style="left:125px; position:relative;" id="4">• A Really Long Entry 4</div>
<div class="event" style="left:175px; position:relative;" id="5">• Entry5</div>
<div class="event" style="left:185px; position:relative;" id="6">• And Even Longer Entry 6</div>
<div class="event" style="left:250px; position:relative;" id="7">• #7</div>
<div class="event" style="left:330px; position:relative;" id="8">• Entry 8</div>
<div class="event" style="left:330px; position:relative;" id="9">• Entry 9</div>
<div class="event" style="left:330px; position:relative;" id="10">• Long Entry 10</div>
<div class="event" style="left:330px; position:relative;" id="11">• Entry 11</div>
<div class="event" style="left:410px; position:relative;" id="12">• Entry 12</div>
<div class="event" style="left:490px; position:relative;" id="13">• Entry 13</div>
</div>

Cycle through multiple divs with prev/next buttons

Right now it cycles through 1 div per click - would it be possible to make this cycle through 4 divs with each click? Completely stumped on it...
HTML:
<div class="outside-buttons">
<div class="prev">Previous</div>
<div class="next">Next</div>
</div>
<div class="parent">
<div class="childclass">some content</div>
<div class="childclass">some content 2</div>
<div class="childclass">some content 3</div>
<div class="childclass">some content 4</div>
<div class="childclass">some content 5</div>
<div class="childclass">some content 6</div>
<div class="childclass">some content 7</div>
<div class="childclass">some content 8</div>
<div class="childclass">some content 9</div>
</div>
jQuery:
var $zDiv = $('.childclass'),
$prNx = $('.prev, .next'),
$btns = $('.zanc > a'),
n = $zDiv.length,
c = 0; // current
d = 4; //number of items
function toggView(){
// content:
$zDiv.hide().eq(c).show();
// buttons:
$prNx.show();
if(c<=0){
$('.prev').hide();
}
if(c>=n-1){
$('.next').hide();
}
}
toggView();
$prNx.click(function(){
c = $(this).hasClass('next') ? ++c : --c;
toggView();
});
$btns.click(function( e ){
c = $(this).index();
toggView();
});
fiddle: https://jsfiddle.net/g1r0ws6w/2/
To always show 4 divs next to each other you need to raise your stepsize within your click() method while displaying more than one item at the same time. I used a for-loop for this purpose.
Concluding You need to adjust the condition to display/hide the next-Button accordingly.
var $zDiv = $('.childclass'),
$prNx = $('.prev, .next'),
$btns = $('.zanc > a'),
n = $zDiv.length,
c = 0; // current
d = 4; //number of items
function toggView(){
// content:
$zDiv.hide();
for(i=0;i<d && i+c<n;i++){
$zDiv.eq(i+c).show();
}
// buttons:
$prNx.show();
if(c<=0){
$('.prev').hide();
}
if(c+3>=n-1){
$('.next').hide();
}
}
toggView();
$prNx.click(function(){
c = $(this).hasClass('next') ? c += 4 : c -= 4;
toggView();
});
$btns.click(function( e ){
c = $(this).index();
toggView();
});
This is a corresponding fiddle.
Here's a fiddle. All you needed was where you have ++c/--c you need to add/subtract your d value.
$prNx.click(function(){
c = $(this).hasClass('next') ? c += d : c -= d;
toggView();
});
You can do this
$prNx.click(function(){
c = $(this).hasClass('next') ? c+4 : c-4;
toggView();
});
I added paging for display as default number (You can set whatever number) . c and d will be your current start and end index of current offset .
var $zDiv = $('.childclass'),
$prNx = $('.prev, .next'),
$btns = $('.zanc > a'),
n = $zDiv.length,
paging = 4;
c = 0; // current
d = paging; //number of items
function toggView(){
// content:
$zDiv.hide();
for(var i = c; i < d; i++) {
$zDiv.eq(i).show();
}
// buttons:
$prNx.show();
if(c == 0){
$('.prev').hide();
}
if(c+paging >= n){
$('.next').hide();
}
}
toggView();
$prNx.click(function(){
if($(this).hasClass('next')) {
c += paging;
d += paging;
} else {
c -= paging;
d -= paging;
}
toggView();
});
.childclass {
height: 100px;
display: inline-block;
background: #e3e3e3;
margin: 10px;
}
.current { display: block; }
.childclass { clear: none !important;
display: inline-block;
white-space: nowrap;
width: 25% !Important;
float: none;
}
.parent {
overflow-x: hidden;
overflow-y: hidden;
white-space: nowrap !Important;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outside-buttons">
<div class="prev">
Previous</div>
<div class="next">
Next</div>
</div>
<div class="parent">
<div class="childclass">
some content
</div>
<div class="childclass">
some content 2
</div>
<div class="childclass">
some content 3
</div>
<div class="childclass">
some content 4
</div>
<div class="childclass">
some content 5
</div>
<div class="childclass">
some content 6
</div>
<div class="childclass">
some content 7
</div>
<div class="childclass">
some content 8
</div>
<div class="childclass">
some content 9
</div>
<div class="childclass">
some content 10
</div>
<div class="childclass">
some content 11
</div>
</div>

Equal height divs in different containers

So I am trying to set equal height for divs that are in different containers. But I am missing something and can't get it to work. Maybe you will spot the problem?
var howMany = $('.comparison-table__labels-wrap .equal-row').length;
for (var i=0; i<howMany; i++) {
var firstBlock = 'equal-row-' + i;
var firstHeight = $(firstBlock).height();
var secondBlock = '.equal-row-' + i + '-child';
var secondHeight = $(secondBlock).height();
if (firstHeight < secondHeight) {
$(firstBlock).css("height", secondHeight);
} else {
$(secondBlock).css("height", firstHeight);
}
}
.row {
border-color: #232323;
border-width: 1px;
border-style: solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container-1">
<div class="row equal-row-0">Row 0</div>
<div class="row equal-row-1">Row 1</div>
<div class="row equal-row-2">Row 2</div>
<div class="row equal-row-3">Row 3</div>
<div class="row equal-row-4">Row 4</div>
<div class="row equal-row-5">Row 5</div>
<div class="row equal-row-6">Row 6</div>
</div>
<div class="container-2">
<div class="row equal-row-0-child">Row 0</div>
<div class="row equal-row-1-child">Row 1</div>
<div class="row equal-row-2-child">Row 2</div>
<div class="row equal-row-3-child">Row 3</div>
<div class="row equal-row-4-child">Row 4</div>
<div class="row equal-row-5-child">Row 5</div>
<div class="row equal-row-6-child">Row 6</div>
</div>
funny example ;)
function equalizeHeights() {
const firstDivs = document.querySelectorAll(".first div")
const secondDivs = document.querySelectorAll(".second div")
const heights = []
for (let div of firstDivs) {
heights.push(div.clientHeight)
}
for (let i = 0; i < heights.length; i++) {
secondDivs[i].style.height = heights[i] + "px"
}
}
equalizeHeights()
function randomlyChangeHeight() {
const divs = document.querySelectorAll(".first div")
const randomNum = Math.floor(divs.length * Math.random())
const randomHeight = Math.floor(50 + 100 * Math.random())
divs[randomNum].style.height = randomHeight + "px"
}
setInterval(() => {
randomlyChangeHeight();
}, 500)
setTimeout(
() =>
setInterval(equalizeHeights, 250), 250)
.first div {
height: 50px;
}
div div {
transition: height 500ms;
border: 1px solid goldenrod;
text-align: center;
padding: 20px;
box-sizing: border-box;
}
.first,
.second {
width: 50%;
float: left;
}
.first div:nth-child(odd),
.second div:nth-child(even) {
background-color: mediumseagreen;
}
<div class="first">
<div>Row</div>
<div>Row</div>
<div>Row</div>
<div>Row</div>
<div>Row</div>
</div>
<div class="second">
<div>Row</div>
<div>Row</div>
<div>Row</div>
<div>Row</div>
<div>Row</div>
</div>
I think you need $('[class^="equal-row"]') to select all divs that you need. See this:
function setNewHeight() {
var howMany = $('[class^="equal-row"]').length;
//console.log(howMany);
for (var i = 0; i < howMany; i++) {
var firstBlock = '.equal-row-' + i;
var firstHeight = $(firstBlock).height();
//console.log(firstBlock, firstHeight);
var secondBlock = '.equal-row-' + i + '-child';
var secondHeight = $(secondBlock).height();
//console.log(secondBlock, secondHeight);
if (firstHeight < secondHeight) {
$(firstBlock).css("height", secondHeight);
} else {
$(secondBlock).css("height", firstHeight);
}
}
}
setNewHeight();
.container-1 div, .container-2 div{
border: 1px solid #ccc
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<div class="container-1">
<div class="container-1">
<div class="equal-row-0">Row 0
<br> test1
</div>
<div class="equal-row-1">Row 1</div>
<div class="equal-row-2">Row 2</div>
<div class="equal-row-3">Row 3</div>
<div class="equal-row-4">Row 4
<br>test2
</div>
<div class="equal-row-5">Row 5</div>
<div class="equal-row-6">Row 6</div>
</div>
<div class="container-2">
<div class="equal-row-0-child">Row 0</div>
<div class="equal-row-1-child">Row 1</div>
<div class="equal-row-2-child">Row 2</div>
<div class="equal-row-3-child">Row 3</div>
<div class="equal-row-4-child">Row 4</div>
<div class="equal-row-5-child">Row 5</div>
<div class="equal-row-6-child">Row 6
<br>test3
</div>
</div>
Its way better to use an abstract class in the elements. And you can do the number definition in a different attribute. This will make the implementation much easier. See the example bellow:
$('.equal-row').each(function () {
var number = $(this).attr('rel');
var parentHeight = $(this).height();
$('.equal-row-child[rel='+number+']').height(parentHeight)
});
.container-1 .equal-row[rel="0"] {
height: 30px;
}
.container-1 .equal-row[rel="1"] {
height: 45px;
}
.container-1 .equal-row[rel="2"] {
height: 15px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<div class="container-1">
<div rel="0" class="equal-row">Row 0</div>
<div rel="1"class="equal-row">Row 1</div>
<div rel="2" class="equal-row">Row 2</div>
</div>
<div class="container-2">
<div rel="0" class="equal-row-child">Row 0</div>
<div rel="1"class="equal-row-child">Row 1</div>
<div rel="2" class="equal-row-child">Row 2</div>
</div>
If you have any problems, let me now :)
So this works just fine:
(function(){
var howManyCols = $('.comparison-table__labels-wrap .equal-row').length;
for (var i=0; i<howManyCols; i++) {
var height1 = $('.equal-row-' + i).outerHeight();
var col1 = $('.equal-row-' + i);
var height2 = $('.equal-row-' + i + '-child').outerHeight();
var col2 = $('.equal-row-' + i + '-child');
if(height1 < height2) {
$(col1).css("height", height2);
} else {
$(col2).css("height", height1);
}
}
})();

pick certain amount of child elements from the dom

i have for example 15 div tags with a certain class name in a page
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
and i can select them using jquery
var targetDivs = $(' .className ');
it is going to return 15 div tags in this case but i want to randomly pick only 9 of them and store them in another variable
You could use Jquery Each and build a unique and random element array.
You could then loop out on your element array to place the elements where you want this randomization to occur.
var divs = [];
var indexs = [];
while(indexs.length < 9){
var num = Math.floor(Math.random() * 9) + 1;
indexs.push(num);
indexs = $.unique(indexs);
}
$('.className').each(function(index, element){
if(indexs[index]){
divs.push($('.className').eq(indexs[index]));
}
});
console.log(divs);
You just need to generate a random number and then use that number as the basis for looping:
var targetDivs = document.querySelectorAll('.className');
var randomAmount = prompt("What is the upper limit for a random number you want?");
var randomNum = Math.floor(Math.random() * randomAmount);
console.log("Random number is: " + randomNum);
for(var i = 0; i < randomNum; ++i){
var randomNode = Math.floor(Math.random() * targetDivs.length);
console.log("Result includes: " + targetDivs[randomNode].textContent);
}
<div class="className">CONTENT 1</div>
<div class="className">CONTENT 2</div>
<div class="className">CONTENT 3</div>
<div class="className">CONTENT 4</div>
<div class="className">CONTENT 5</div>
<div class="className">CONTENT 6</div>
<div class="className">CONTENT 7</div>
<div class="className">CONTENT 8</div>
<div class="className">CONTENT 9</div>
<div class="className">CONTENT 10</div>
<div class="className">CONTENT 11</div>
<div class="className">CONTENT 12</div>
<div class="className">CONTENT 13</div>
<div class="className">CONTENT 14</div>
<div class="className">CONTENT 15</div>
One approach I'd suggest is the use of a simple plugin:
(function($) {
// naming the new plugin ('getRandom'):
$.fn.getRandom = function(n) {
// creating an Array by passing the jQuery collection
// the 'this' passed to the function to the get()
// method, which takes the passed-in collection
// and returns a jQuery Array:
var collection = this.get(),
// creating an Array, using an Array-literal:
result = [],
// initialising a variable for use, later:
rand;
// converting the passed-in argument, 'n', into a
// base-10 ('10') Number, using parseInt() (this
// does no harm if 'n' is already a Number, but
// ensures that, if a String is passed in ('3' for
// example) we get a Number back out:
n = parseInt(n, 10);
// while n is still truthy (so non-zero):
while (n) {
// we generate a random number in the range of
// 0 to the length of the collection Array:
rand = Math.floor(Math.random() * collection.length);
// we use the random number as an index, and
// push the Array-element at that index in the
// collection Array into the result Array:
result.push(collection[rand]);
// we then remove the element at that index from the
// collection Array, passing in the same index ('rand')
// and deleting one element ('1'):
collection.splice(rand, 1);
// decrement the n variable:
n--;
}
// convert the result Array of elements back into
// object, and return that object to the calling
// context for chaining:
return $(result);
}
})(jQuery);
(function($) {
$.fn.getRandom = function(n) {
var collection = this.get(),
result = [],
rand;
n = parseInt(n, 10);
while (n) {
rand = Math.floor(Math.random() * collection.length);
result.push(collection[rand]);
collection.splice(rand, 1);
n--;
}
return $(result);
}
})(jQuery);
$('.className').getRandom(10).css({
'opacity': '0.2'
})
.className {
display: inline-block;
width: 3em;
height: 3em;
box-sizing: border-box;
border: 2px solid #000;
text-align: center;
overflow: hidden;
counter-increment: boxCounter;
position: relative;
}
.className::after {
content: counter(boxCounter, decimal-leading-zero);
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
JS Fiddle demo.
As most of the jQuery plugin above is written in plain JavaScript this is, of course, similarly easily-possible in plain JavaScript, albeit as a function, rather than a method or prototype-extension (although, if you wish to do it that way, it remains easily-possible to do so, albeit not necessarily advised):
function getRandomFrom(haystack, n) {
// ensuring that we have an Array, assuming we're
// passed an Array-like Object (such as a NodeList,
// HTMLCollection or, even, an Array):
haystack = Array.from(haystack);
// ensuring that the variable n is a Number, using
// parseInt():
n = parseInt(n, 10);
// creating an empty Array:
var result = [],
// initialising a variable for later use:
rand;
// while n is truthy (non-zero):
while (n) {
// we generate a random number between 0 and
// the Array-length:
rand = Math.floor(Math.random() * haystack.length);
// we use the random number as an index for the Array,
// and push the Array-element held at that index to the
// result Array:
result.push(haystack[rand]);
// we remove that Array-element from the Array, using
// Array.prototype.splice():
haystack.splice(rand, 1);
// decrement n, to ensure we don't have an infinite
// while loop:
n--;
}
// return the result Array to the calling context:
return result;
}
function getRandomFrom(haystack, n) {
haystack = Array.from(haystack);
n = parseInt(n, 10);
var result = [],
rand;
while (n) {
rand = Math.floor(Math.random() * haystack.length);
result.push(haystack[rand]);
haystack.splice(rand, 1);
n--;
}
return result;
}
var elems = document.querySelectorAll('.className');
getRandomFrom(elems, 5).forEach(el => el.classList.add('selected'));
.className {
display: inline-block;
width: 3em;
height: 3em;
box-sizing: border-box;
border: 2px solid #000;
text-align: center;
overflow: hidden;
counter-increment: boxCounter;
position: relative;
}
.className::after {
content: counter(boxCounter, decimal-leading-zero);
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: #fff;
}
.className.selected {
opacity: 0.25;
border-color: red;
}
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
<div class="className">CONTENT</div>
References:
JavaScript:
Array.prototype.splice().
Array.prototype.splice().
Array.prototype.push().
Array.prototype.splice().
Arrow functions.
Element.classList.
Math.floor().
Math.random().
parseInt().
jQuery:
css().
Bibliography:
"How to Create a Basic Plugin."
Example below using ES6 Map:
let results = new Map();
for(let i = 0; i < 9; i++) {
let index= null;
while(index=== null || results.has(index)) {
index= Math.floor(Math.random() * 9);
}
results.set(index, document.querySelectorAll('.className')[index]);
}
for (let result of results.values()) {
console.log(result.textContent)
}
<div class="className">CONTENT 1</div>
<div class="className">CONTENT 2</div>
<div class="className">CONTENT 3</div>
<div class="className">CONTENT 4</div>
<div class="className">CONTENT 5</div>
<div class="className">CONTENT 6</div>
<div class="className">CONTENT 7</div>
<div class="className">CONTENT 8</div>
<div class="className">CONTENT 9</div>
<div class="className">CONTENT 10</div>
<div class="className">CONTENT 11</div>
<div class="className">CONTENT 12</div>
<div class="className">CONTENT 13</div>
<div class="className">CONTENT 14</div>
<div class="className">CONTENT 15</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>

Categories

Resources