This is more in the realm of "is this possible without a stupid amount of code?" and to get some ideas of how to go about it. If I had a css representation of a dinner table, and wanted to position 4-10 chairs around it, I might create the divs like this (inline to show what I'm doing compactly)
<div style="position:relative;width:200px;height:200px;">
<div id="table" style="position:absolute;width:100px;height:100px;"> </div>
<div id="chair1" style="position:absolute;width:10px;height:10px;"> </div>
<div id="chair2" style="position:absolute;width:10px;height:10px;"> </div>
<div id="chair3" style="position:absolute;width:10px;height:10px;"> </div>
<div id="chair4" style="position:absolute;width:10px;height:10px;"> </div>
</div>
Where there's a div to hold it all and a div to represent 4 chairs and a table. But what if my dynamically created code needed to create 6, 8, or 10 chairs? How would I go about spacing them dynamically off the table at an even rate around the table? Thoughts?
You need JavaScript in order to set the chairs. Something to get you started:
HTML:
<div id="room">
<div id="table"></div>
</div>
CSS:
#room {
position:relative;
width:200px;
height:200px;
}
#table {
background:blue;
position:absolute;
width:100%;
height:100%;
}
.chair {
background:red;
position:absolute;
width:10px;
height:10px;
}
JavaScript using jQuery:
var $chair = $('<div class="chair">');
var $room = $('#room');
var top = 0;
var left = 0;
var stepSize = 20;
var createChairs = function(amount) {
for (var i = 0; i < amount; i++) {
var newChair = $chair.clone();
newChair.css({top: top, left: left});
$room.append(newChair);
left = left + stepSize;
}
}
createChairs(10);
You create a function which you pass the amount of chairs you wish, then you go around the border and set as much chairs as wished. This example does not include the border handling yet i.e. it will just set chairs from the top left to the top right. But handling borders should be easy doable with a few conditions. You should have gotten the idea.
Fiddle: https://jsfiddle.net/s0oady2q/
I really don't know how you would be able to do this without at least a little bit of JavaScript. In fact, even if you tried to use some kind of calc() magic, you would still run into the issue that calc() doesn't support sin() or cos() calls.
Here is a small, pure JavaScript way to get what you want though:
/*jslint browser:true*/
(function(doc) {
"use strict";
var TABLE_RADIUS = 100;
function each(arr, cb) {
return Array.prototype.forEach.call(arr, cb);
}
function deg2rad(deg) {
return ((deg * Math.PI) / 180);
}
function pos(deg, r) {
return {
x: (r * (Math.cos(deg2rad(deg)))),
y: (r * (Math.sin(deg2rad(deg))))
};
}
function chairIterator(chair, chairIndex, chairArr) {
var circPos = pos(chairIndex / chairArr.length * 360, TABLE_RADIUS);
chair.style.top = circPos.y + "px";
chair.style.left = circPos.x + "px";
}
function tableIterator(table) {
var chairs = table.querySelectorAll(".chair");
each(chairs, chairIterator);
}
function main() {
var tables = document.querySelectorAll(".table");
each(tables, tableIterator);
}
doc.addEventListener("DOMContentLoaded", main);
}(document));
.table {
display: inline-block;
position: absolute;
width: 10px;
height: 10px;
top: 50%;
left: 50%;
background-color: red;
}
.table .chair {
position: absolute;
width: 10px;
height: 10px;
background-color: blue;
}
<div class="table">
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
<div class="chair"></div>
</div>
Related
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>
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>
The html, js, css example is https://jsfiddle.net/t9mfmaa3/5/.
/* Latest compiled and minified JavaScript included as External Resource */
$(function() {
var $sidebar = $("#e"),
$window = $(window),
$offset = $sidebar.offset(),
$topPadding = 15;
$window.scroll(function() {
if ($window.scrollTop() > $offset.top) {
$sidebar.stop().animate({
marginTop: $window.scrollTop() - $offset.top + $topPadding
});
} else {
$sidebar.stop().animate({
marginTop: 0
});
}
});
});
/* Latest compiled and minified CSS included as External Resource*/
/* Optional theme */
#import url('//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css');
body {
margin: 10px;
}
#c {
background-color: red;
height: 2400px
}
#e {
background-color: lightblue;
height: 600px
}
#b {
height: 2400px;
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<div class="container">
<div class="row">
<div class="a">
<div id="b" class="column col-xs-3 col-sm-3">
<div id="e" class="">
blue
</div>
</div>
<div id="c" class="center_column col-xs-9 col-sm-9">
red
</div>
</div>
</div>
</div>
I tried to make blue block not exceed yellow block which means the blue one always in yellow block. My idea is to set code to detect block yellow and block blue. But I didn't success. Anybody has any suggestion? Thanks
If you are already using bootstrap, you may as well use their affix javascript.
getbootstrap.com
Here is an example:
jsfiddle.net
$(function() {
var $sidebar = $("#e"),
$body = $('body'),
$parent = $('#b'),
topPadding = 15,
offset=$sidebar.offset();
$sidebar.affix({
offset: {
top: function() {
return $parent.offset().top - topPadding;
},
bottom: function() {
return $(document.body).height() - ($parent.offset().top + $parent.outerHeight());
}
}
});
});
You might notice it act a little weird and jumpy near the end, but that should go away when using it on a real site (instead of inside an iframe)
<body>
<div id = "SiteContainer">
<div id = "NavigationButtons"></div>
<div id = "ShowReelContainer">
<div id= "NavigationBackward" name = "back" onclick="setPosition();">x</div>
<div id= "NavigationForward" name = "forward" onclick="setPosition();">y</div>
<div id = "VideoWrapper">
<div id = "SlideShowItem">
<img src="Images/A.png" alt="A"></img>
</div>
<div id = "SlideShowItem">
<img src="Images/B.png" alt="B"></img>
</div>
<div id = "SlideShowItem">
<img src="Images/C.png" alt="C" ></img>
</div>
</div>
</div>
</div>
<script>
var wrapper = document.querySelector("#VideoWrapper");
function setPosition(e)
{
if(e.target.name = "forward")
{
if!(wrapper.style.left = "-200%")
{
wrapper.style.left = wrapper.style.left - 100%;
}
}
else
{
if(e.target.name = "back")
{
if!(wrapper.style.left = "0%")
{
wrapper.style.left = wrapper.style.left + 100%;
}
}
}
}
</script>
</body>
Hi, I am very new to javascript. What I am trying to do, is change the x-position of a div when another div (NavigationForward or NavigationBackward) is clicked. However it does not appear to do anything at all. Basically if the div with name forward is clicked, I want to translate the VideoWrapper -100% from it's current position and +100% when "back". The css div itself VideoWrapper has a width of 300%. Inside this div as you can see is a SlideShowItem which is what will change. Perhaps I am adding and subtracting 100% the wrong way?
EDIT:
Thanks everyone for helping me out with this...I had just one more query, I am trying to hide the arrows based on whether the wrapper is at the first slide or the last slide. If its on the first slide, then I'd hide the left arrow div and if it's on the last, I'd hide the right arrow, otherwise display both of em. Ive tried several ways to achieve this, but none of em work, so Ive resorted to using copies of variables from the function that works. Even then it does not work. It appears that my if and else if statements always evaluate to false, so perhaps I am not retrieving the position properly?
function HideArrows()
{
var wrapper2 = document.getElementById("VideoWrapper");
var offset_x2 = wrapper2.style.left;
if(parseInt(offset_x2,10) == max_x)
{
document.getElementById("NavigationForward").display = 'none';
}
else if(parseInt(offset_x2,10) == min_x)
{
document.getElementById("NavigationBackward").display = 'none';
}
else
{
document.getElementById("NavigationForward").display = 'inline-block';
document.getElementById("NavigationBackward").display = 'inline-block';
}
}
//html is the same except that I added a mouseover = "HideArrows();"
<div id = "ShowReelContainer" onmouseover="HideArrows();">
To achieve this type o slider functionality your div VideoWrapper must have overflow:hidden style, and your SlideShowItemdivs must have a position:relative style.
Then to move the slides forward or backward you can use the style left which allows you to move the divs SlideShowItem relative to it's parent VideoWrapper.
I've tested this here on JSFiddle.
It seems to work as you described in your question, although you may need to do some adjustments, like defining the width of your slides, how many they are and so on.
For the sake of simplicity, I defined them as "constants" on the top of the code, but I think you can work from that point on.
CSS
#VideoWrapper{
position:relative; height:100px; white-space:nowrap;width:500px;
margin-left:0px; border:1px solid #000; overflow:hidden; }
.SlideShowItem{
width:500px; height:100px;display:inline-block;position:relative; }
#NavigationForward, #NavigationBackward{
cursor:pointer;float:left; background-color:silver;margin-right:5px;
margin-bottom:10px; text-align:center; padding:10px; }
HTML
<div id = "SiteContainer">
<div id = "NavigationButtons">
</div>
<div id = "ShowReelContainer">
<div id= "NavigationBackward" name = "back" onclick="setPosition('back');">prev</div>
<div id= "NavigationForward" name = "forward" onclick="setPosition('forward');">next</div>
<div style="clear:both;"></div>
<div id = "VideoWrapper">
<div class= "SlideShowItem" style="background-color:blue;">
Slide 1
</div>
<div class = "SlideShowItem" style="background-color:yellow;">
Slide 2
</div>
<div class = "SlideShowItem" style="background-color:pink;">
Slide 3
</div>
</div>
</div>
</div>
JavaScript
var unit = 'px'; var margin = 4; var itemSize = 500 + margin; var itemCount = 3; var min_x = 0; var max_x = -(itemCount-1) * itemSize;
function setPosition(e) {
var wrapper = document.getElementById("VideoWrapper");
var slides = wrapper.getElementsByTagName('div');
var offset_x = slides[0].style.left.replace(unit, '');
var curr_x = parseInt(offset_x.length == 0 ? 0 : offset_x);
if(e == "forward")
{
if(curr_x <= max_x)
return;
for(var i=0; i<slides.length; i++)
slides[i].style.left= (curr_x + -itemSize) + unit;
}
else if(e == "back")
{
if(curr_x >= min_x)
return;
for(var i=0; i<slides.length; i++)
slides[i].style.left= (curr_x + itemSize) + unit;
} }
After you analyze and test the code, I don't really know what's your purpose with this, I mean, you maybe just playing around or trying to develop something for a personal project, but if you are looking for something more professional avoid to create things like sliders on your own, as there are tons of plugins like this available and well tested out there on the web.
Consider using jQuery with NivoSlider, it works like a charm and is cross browser.
I would recommend using jQuery, this will reduce your coding by quite a bit. Can read more here: http://api.jquery.com/animate/
I've created a simple fiddle for you to take a look at. This example uses the .animate() method to reposition two div elements based on the CSS 'left' property.
CSS:
#container {
position: absolute;
left: 1em;
top: 1em;
right: 1em;
bottom: 1em;
overflow: hidden;
}
#one, #two {
position: absolute;
color: white;
}
#one {
background: pink;
width: 100%;
top:0;
bottom:0;
}
#two {
background: blue;
width: 100%;
left: 100%;
top:0;
bottom:0;
}
HTML:
<div id="container">
<div id="one">Div One</div>
<div id="two">Div Two</div>
</div>
JavaScript/jQuery:
var one, two, container;
function animateSlides(){
one.animate({
left : '-100%'
}, 1000, function(){
one.animate({
left : 0
}, 1000);
});
two.animate({
left : 0
}, 1000, function(){
two.animate({
left:'100%'
}, 1000);
});
};
$(function(){
one = $('#one');
two = $('#two');
container = $('#container');
setInterval(animateSlides, 2000);
});
JSFiddle Example: http://jsfiddle.net/adamfullen/vSSK8/3/
I am trying to create multiple boxes along the top of the page using javascript. I have one box but cannot figure out how to get multiple along the top of the page. This is what I have so far:
<html>
<head>
<title>Boxes on Boxes on Boxes</title>
<link rel="stylesheet" type="text/css" href="boxes.css">
<script language="JavaScript">
el=document.getElementById("box1");
width=window.innerWidth-50;
height=window.innerHeight-50;
el.style.left=width*Math.random();
el.style.top=height*Math.random();
el=document.getElementById("box2");
width=window.innerWidth-50;
height=window.innerHeight-50;
el.style.right=width*Math.random();
el.style.top=height*Math.random();
el=document.getElementById("box3");
width=window.innerWidth-50;
height=window.innerHeight-50;
el.style.middle=width*Math.random();
el.style.top=height*Math.random();
</script>
</head>
<body>
<div id="box1">
First box
</div>
<div id="box2">
Second box
</div>
<div id="box3">
Third box
</div>
</body>
</html>
Here is the CSS that I have:
#box1{
background-color:orange;
padding:5px;
width:50px;
height:50px;
position:absolute;
left=100px;
top=100px;
}
#box2{
background-color:blue;
padding:5px;
width:50px;
height:50px;
position:absolute;
left=100px;
top=100px;
}
#box3{
background-color:green;
padding:5px;
width:50px;
height:50px;
position:absolute;
left=100px;
top=100px;
}
You need to either move the <script> element to the end or wrap your code in a DOM ready or onload handler, because otherwise getElementById() won't find any elements because they won't have been parsed yet.
Then you need to include a unit (e.g., "px") in the left and top style properties.
Also there's no need to recalculate the width and height for each box since you're doing the same calculation for each. (And you should declare your variables with var, but although good practice that isn't essential to make it work.)
Here's a working version:
var el=document.getElementById("box1");
var width=window.innerWidth-50;
var height=window.innerHeight-50;
el.style.left=width*Math.random() + "px";
el.style.top=height*Math.random() + "px";
el=document.getElementById("box2");
el.style.right=width*Math.random() + "px";
el.style.top=height*Math.random() + "px";
el=document.getElementById("box3");
el.style.middle=width*Math.random() + "px";
el.style.top=height*Math.random() + "px";
Demo: http://jsfiddle.net/m3Gg3/
Also the left and top properties in your CSS should use : not =.
It is difficult to understand what you want, maybe this?.
<script>
window.onload = function() {
var titles = ["First box", "Second box", "Third box"]
var width=window.innerWidth-50
var height=window.innerHeight-50-120
for (var i = 0; i < titles.length; i++) {
var el = document.createElement('div')
console.log(el)
el.innerHTML = titles[i]
el.style.position = "absolute"
el.style.border = "1px solid rgb(0,0,0)"
el.style.left= (width / titles.length) * i
el.style.top=0
el.style.width = width / titles.length
el.style.height = "120px"
document.body.appendChild(el);
}
for (var i = 0; i < titles.length; i++) {
var el = document.createElement('div')
console.log(el)
el.innerHTML = titles[i]
el.style.position = "absolute"
el.style.border = "1px solid rgb(0,0,0)"
el.style.left=0
el.style.top=(height / titles.length) * i + 120
el.style.width = "120px"
el.style.height = height / titles.length
document.body.appendChild(el);
}
}
</script>
<html>
<head>
<title>Boxes on Boxes on Boxes</title>
<style type="text/css">
#box_group1, #box_group2, #box_group3, #box_group4, #textbook {
position:absolute;
left:100px;
top:100px;
}
#box1, #box2, #box3, #box10, #box11, #box12 {
padding:5px;
width:50px;
height:50px;
cursor:pointer;
float:left;
}
#box4, #box5, #box6, #box7, #box8, #box9 {
padding:5px;
width:50px;
height:50px;
cursor:pointer;
}
#box1, #box4, #box7, #box10{
background-color:orange;
}
#box2, #box5, #box8, #box11 {
background-color:blue;
}
#box3, #box6, #box9, #box12{
background-color:green;
}
#box4, #box7 {
font-family: Arial;
}
#box5, #box8 {
font-family: Courier;
}
#box6, #box9 {
font-family: Tahoma;
}
#textbook {
padding: 5px;
background-color:red;
}
</style>
<script language="JavaScript">
width=window.innerWidth;
height=window.innerHeight;
function boxes() {
document.getElementById("box_group1").style.left=(width-document.getElementById("box_group1").offsetWidth)/2;
document.getElementById("box_group2").style.top=(height-document.getElementById("box_group2").offsetHeight)/2;
document.getElementById("box_group3").style.left=width-100-document.getElementById("box_group3").offsetWidth;
document.getElementById("box_group3").style.top=(height-document.getElementById("box_group3").offsetHeight)/2;
document.getElementById("box_group4").style.left=(width-document.getElementById("box_group4").offsetWidth)/2;
document.getElementById("box_group4").style.top=height-100-document.getElementById("box_group4").offsetHeight;
document.getElementById("textbook").style.left=(width-document.getElementById("textbook").offsetWidth)/2;
document.getElementById("textbook").style.top=(height-document.getElementById("textbook").offsetHeight)/2;
}
function colorChange(field,group) {
switch (group) {
case 1:
document.getElementById("box2").style.backgroundColor = field.innerText;
break;
case 4:
document.getElementById("box11").style.backgroundColor = field.innerText;
break;
}
}
function fontChange(field,group) {
switch (group) {
case 2:
document.getElementById("box5").style.fontFamily = field.innerText;
break;
case 3:
document.getElementById("box8").style.fontFamily = field.innerText;
break;
}
}
</script>
</head>
<body onload="boxes()">
<div id="box_group1">
<div id="box1" onclick="colorChange(this,1)">
Orange
</div>
<div id="box2" onclick="colorChange(this,1)">
Blue
</div>
<div id="box3" onclick="colorChange(this,1)">
Green
</div>
</div>
<div id="box_group2">
<div id="box4" onclick="fontChange(this,2)">
Arial
</div>
<div id="box5" onclick="fontChange(this,2)">
Courier
</div>
<div id="box6" onclick="fontChange(this,2)">
Tahoma
</div>
</div>
<div id="box_group3">
<div id="box7" onclick="fontChange(this,3)">
Arial
</div>
<div id="box8" onclick="fontChange(this,3)">
Courier
</div>
<div id="box9" onclick="fontChange(this,3)">
Tahoma
</div>
</div>
<div id="box_group4">
<div id="box10" onclick="colorChange(this,4)">
Orange
</div>
<div id="box11" onclick="colorChange(this,4)">
Blue
</div>
<div id="box12" onclick="colorChange(this,4)">
Green
</div>
</div>
<div id="textbook">Textbook</div>
</body>
</html>
Try this using jQuery :
Here the boxes should be created dynamically and without naming the id's hardcoded it also should be done in a better way with your code. It's easier now as you are creating 4 boxes, what about 100 or more. So it's wise to always take the better way to maintain scalability of our work.
HTML :
<div id="mainDiv">
</div>
CSS :
// general css for all divs inside mainDiv
#mainDiv div{
padding:5px;
width:50px;
height:50px;
position:absolute;
left=100px;
top=100px;
float : left;
}
jQuery :
$(document).ready(function(){
// taking a color array
var colorArray = new Array("red", "green", "gray", "blue");
// loop through as many boxes you want to create
for(var i = 1; i <= colorArray.length; i++){
$("#mainDiv").append("<div id=Box" + i + "></div>");
//changing the background-color
$("#Box"+ i).css("background-color", colorArray[i-1]);
}
});
Demo
Here's Another thread explaining similar Case,
And It's Solution