I would like to make all these boxes behave like they are floated. However, they can’t be, they need to be absolutely positioned in order for me to interact with this positioned number.
Here's my attempt:
var $item = $('#wrapper div'),
len = $item.length,
itemWidth = $item.innerWidth(),
winWidth = $('#wrapper').innerWidth(),
cols = Math.floor(winWidth / itemWidth),
moveX = itemWidth + 10;
function absPos() {
for (var i = 0; i < len; i += 1) {
$('.item-' + i).css({
'position' : 'absolute',
'left' : moveX * i
});
}
}
I just can't figure out how to wrap them to fit, and also re-position when the window is resized.
Here’s a demo. http://jsfiddle.net/Fgcqs/3/ . If you un-commment the absPos() function you will see my start.
Thanks for any help!
I have edited your jsfiddle to move the items like they are floated. It assumes your margins and widths are the same for each of the divs within wrapper and will automatically work out the width and height of the spacing if your css changes
var wrapper = $('#wrapper'),
items = wrapper.children('div'),
len = items.length,
itemWidth = items.innerWidth() + parseInt(items.css('margin-left')) + parseInt(items.css('margin-right')),
itemHeight = items.innerHeight() + parseInt(items.css('margin-top')) + parseInt(items.css('margin-bottom'));
items.css('float', 'none');
function absPos() {
var cols = Math.floor(wrapper.width() / itemWidth);
items.each(function() {
var left = ($(this).index() % cols) * itemWidth; //the bit in brackets calculates which column the div should be in (the remainder of the current index of your item divided by the number of columns per row), then you times that by item width as worked out above, you use the index as this will allow you to start at left:0
var height = Math.floor($(this).index() / cols) * itemHeight //the bit in brackets calculates which row the div should be in, then you times that by item height as worked out above, you use the Math.floor as this will allow you to start at top:0. Should have really called this top!
$(this).css({
'position' : 'absolute',
'top': height,
'left': left
});
});
wrapper.height((Math.ceil(len / cols)) * itemHeight);
}
$(window).resize(function() {
absPos();
});
absPos();
http://jsfiddle.net/Fgcqs/12/
You have to keep track of both column index and row index: once column index * item width exceeds window width, reset column index and increment row index to simulate next row. Here's simple example of this approach:
function absPos() {
var colIndex = 0;
var rowIndex = 0;
for (var i = 0; i < len; i += 1) {
if (moveX * colIndex + itemWidth > winWidth) {
colIndex = 0;
rowIndex++;
top += itemHeight + 10;
}
var left = moveX * colIndex;
var top = moveY * rowIndex;
$('.item-' + i).css({
'position' : 'absolute',
'left' : left,
'top' : top
});
colIndex++;
}
}
http://jsfiddle.net/N4S4L/1/
You should check if your left value plus the width of an item exceeds the width of your container, in which case introduce a top value and reset left to 0 to start building a new row.
Related
I do not know much about css, but I think this code could help me generate a marquee. basically I want the animation that is done with the boxes, be done with the texts.
My main problem occurs with the animation, it is not very fluid, I want it to be more fluid and it starts from the end of the container to the left. How can I do it? I would be very grateful.
http://jsfiddle.net/joof5dhx/
<div id="horizontalScroller">
<div>it is a message a little more of 100 characteres</div>
<div>it is a message a little more of 110 characteres</div>
<div>it is a message a little more of 120 characteres</div>
<div>it is a message a little more of 130 characteres</div>
</div>
window.horizontalScroller = function($elem) {
var left = parseInt($elem.css("left"));
var temp = -1 * $('#horizontalScroller > div').height();
if(left < temp) {
left = $('#horizontalScroller').height()
$elem.css("left", left);
}
$elem.animate({ left: (parseInt(left)-60) }, 900, function () {
window.horizontalScroller($(this))
});
}
$(document).ready(function() {
var i = 0;
$("#horizontalScroller > div").each(function () {
$(this).css("left", i);
i += 60;
window.horizontalScroller($(this));
});
});
http://jsfiddle.net/hhcbtyyg/
You could just:
window.horizontalScroller = function($elem)
{
var left = parseInt($elem.css("left"));
$elem.animate({ left: (parseInt(left)-60) }, 900, function ()
{
// get the current left of the element
var currentLeft = parseInt($(this).css("left"));
// get the width of the element
var width = $(this).width();
// get the container
var container = $(this).parent("#horizontalScroller");
// get the width of the container
var containerWidth = $(container).width();
// check if the element goes out of the view item X + item w < 0
if ((currentLeft + width) <= 0)
{
// put it on the opposite side: simply container w + item w
$(this).css("left", (containerWidth + width) + "px");
}
window.horizontalScroller($(this))
});
}
I just don't understand why you use height in your code above. If there is something I don't know let me know.
UPDATED:
To make the items appear on the leftmost by default:
$(document).ready(function() {
var container = $("#horizontalScroller");
var children = $(container).children();
var containerW = $(container).width();
// Loop through each item of container
for (var i = 0; i < children.length; i++)
{
var item = children[i];
var itemW = $(item).width();
// this is simply the space between them, remove if you don't need it
var padding = 10 * (i + 1);
// simply: padding + Container Width + (Item Width * (i + 1))
// (Item Width * (i + 1)) because you need to position each element beside each other.
$(item).css("left", (padding + containerW + itemW * (i + 1)) + "px");
window.horizontalScroller($(item));
}
});
your updated fiddle
hope that helps
Hi checked this version of your jsfiddle, i did some modificaitons, since your animation starts from whatever the value of height is your div had. check this I tried to match the height of your css and width in your css, i just noticed that the "left" var in your js gets the height of your element.
CSS:
#horizontalScroller {
position: absolute;
width:300px;
height: 300px;
border: 1px solid red;
overflow: hidden;
}
Maybe you can get some tips how to accomplish it in responsive way.
JSFIDDLE
I have a div that moves across the page on a keydown event listener. I'm trying to find a way to make it stop after either a certain number of keydowns (calculated by step * clicks > screen size) or when the animated div reaches the end of the screen, which I guess would need to be responsive to screen size.
Currently the animate continues to fire on keydown and the div will scroll off out of view.
An example of this is the second demo on this page: http://api.jquery.com/animate/
You can click left or right until the block moves out of view. How would I contain it to it's uh, container?
here's my jsfiddle: https://jsfiddle.net/vidro3/1a8tnuer/
var count1 = 0;
var count2 = 0;
// 1. Get two divs to move on different key presses;
$(function(){
$(document).keydown(function(e){
switch (e.which){
case 39: //lright arrow key
count2++;
$('.box2').animate({
left: '+=50'
});
break;
case 90: //z key
count1++;
$('.box1').animate({
left: '+=50'
});
break;
}
});
});
// 2. Get one div to move on button click;
$(function(){
$( '#start' ).click(function() {
alert('Chug that Duff!');
var rabbit = $(".rabbit");
rabbit.animate({left: '+=100%'}, 'slow');
});
});
$(function(){
var winPos = $('#background').width();
if (winPos/50 > count1)
alert('Homer Wins!');
else if (winPos/50 > count2)
alert('Barney Wins!');
});
Just add a conditional to the animation:
var maxLeft = $(window).width() - $('.box1').width();
// If the box's x-position is less than the max allowed, then animate
if ($('.box1').position().left < maxLeft) {
$('.box1').animate({
left: '+=50'
});
}
The value (window width - box width) is the point at which the box is at the end of the screen. Note that your step size may take the box past the end of the screen depending on the current window size (it's probably not divisible by 50, for example), so you may want something like this instead:
var stepSize = 50;
var maxLeft = $(window).width() - $('.box1').width();
// If the box's x-position is less than the max allowed, then animate
if ($('.box1').position().left < maxLeft) {
var diff = maxLeft - $('.box1').position().left;
// If the next step would take the box partially off the screen
if (diff < stepSize) {
$('.box1').animate({
left: '+=' + diff
});
} else {
$('.box1').animate({
left: '+=' + stepSize
});
}
}
Edit: here's a shorter version using the ternary operator:
var stepSize = 50;
var maxLeft = $(window).width() - $('.box1').width();
// If the box's x-position is less than the max allowed, then animate
if ($('.box1').position().left < maxLeft) {
var diff = maxLeft - $('.box1').position().left;
$('.box1').animate({
left: '+=' + (diff < stepSize ? diff : stepSize)
});
}
I adapted this code to create a large div which scrolls horizontally inside a smaller div, depending on the position of the mouse.
You can see my example here.. http://thetally.efinancialnews.com/tallyassets/20years/index.html
What I am trying to achieve is for the inner (yellow) div to stop at a maximum of left:0px, in other words the far left of the yellow div will become stuck to the far left of the outer div if you go that far.
I tried to implement this with an 'if else' statement, however as this piece of code gets run every 30th of a second it creates a strange result, which I can't find a solution for. I'm sure its very simple but its stumped me
You can see my code here...
var x=0,
rate=0,
maxspeed=10;
var backdrop = $('.container');
var years = $('.events');
$('.direction', backdrop).mousemove(function(e){
var $this = $(this);
var left = $this.is('.left');
if (left){
var w = $this.width();
rate = (w - e.pageX - $(this).offset().left + 1)/w;
} else {
var w = $this.width();
rate = -(e.pageX - $(this).offset().left + 1)/w;
}
});
backdrop.hover(function(){
var scroller = setInterval( moveBackdrop, 30 );
$(this).data('scroller', scroller);
},
function(){
var scroller = $(this).data('scroller');
clearInterval( scroller );
});
function moveBackdrop(){
if ( parseInt(years.css("left"), 10) <= 0 ) {
x += maxspeed * rate;
var newpos = x+'px';
years.css('left',newpos);
} else {
years.css('left','0');
}
}
The code in question is right here at the end^
Is this what you were trying to do?
function moveBackdrop(){
if ( parseInt(years.css("left"), 10) <= 0 && rate < 0 ) {
// Means that the element is already at left: 0 or less,
// and we are trying to move it even more left with rate being negative.
// So keep the element at left: 0
years.css('left','0');
} else {
x += maxspeed * rate;
var newpos = x+'px';
years.css('left',newpos);
}
}
Extra note for future: parseInt uses base 10 by default :) so parseInt("20px") will equal 20
Final Edit: Ah there is an even better way to do it.
function moveBackdrop(){
x += maxspeed * rate;
if( x < 0 ) x = 0; // If left less than 0, fix it at 0
var newpos = x+'px';
years.css('left',newpos);
}
I'm working on a portal/dashboard type interface which has panels/widgets that can be freely dragged around the dashboard space as long as they don't overlay any other panels. New panels can be added to the dashboard via a menu containing all available panels, and when a menu item is clicked, the panel is placed into the dashboard. The panels currently occupying the dashboard space are all represented in an object like this:
{
'panel_1': { top: 0, left: 0, width: 300, height: 350 },
'panel_2': { top: 0, left: 370, width: 200, height: 275 },
'panel_3': { top: 370, left: 0, width: 275, height: 400 },
...
}
My question is, what is a valid algorithm that would, when the user clicks one in the menu, correctly place a new panel (of a given width and height) in unoccupied space that is closest to a left and top (x and y) value of 0, 0, without overlapping any of the existing panels?
I think, simple bruteforce algorithm will fit you. As I remember, fit rectangle solve another problem
Iterate over your dashboard axis to find out, whether you can place your rectangle, until X < rectangle.widh + dashboard.width, same for Y.
Foreach X,Y on dashboard iterate over every panel to find whether they overlap. You can apply some optimization, to decrease amount of iteration. If panel overlap rectangle, you can increase X or Y(which is in nested loop) not by 1, but by width or height of panel.
In most cases, you will not make dashboard.width*dashboard.height*panel.count iteration. With some optimization, it will find best fit rather quick
I know this is an old question but if anyone wants a proof of concept then it looks like this:
function findSpace(width, height) {
var $ul = $('.snap-layout>ul');
var widthOfContainer = $ul.width();
var heightOfContainer = $ul.height();
var $lis = $ul.children('.setup-widget'); // The li is on the page and we dont want it to collide with itself
for (var y = 0; y < heightOfContainer - height + 1; y++) {
var heightOfShortestInRow = 1;
for (var x = 0; x < widthOfContainer - width + 1; x++) {
//console.log(x + '/' + y);
var pos = { 'left': x, 'top': y };
var $collider = $(isOverlapping($lis, pos, width, height));
if ($collider.length == 0) {
// Found a space
return pos;
}
var colliderPos = $collider.position();
// We have collided with something, there is no point testing the points within this widget so lets skip them
var newX = colliderPos.left + $collider.width() - 1; // -1 to account for the ++ in the for loop
x = newX > x ? newX : x; // Make sure that we are not some how going backwards and looping forever
var colliderBottom = colliderPos.top + $collider.height();
if (heightOfShortestInRow == 1 || colliderBottom - y < heightOfShortestInRow) {
heightOfShortestInRow = colliderBottom - y; // This isn't actually the height its just the distance from y to the bottom of the widget, y is normally at the top of the widget tho
}
}
y += heightOfShortestInRow - 1;
}
//TODO: Add the widget to the bottom
}
function isOverlapping($obsticles, tAxis, width, height) {
var t_x, t_y;
if (typeof (width) == 'undefined') {
// Existing element passed in
var $target = $(tAxis);
tAxis = $target.position();
t_x = [tAxis.left, tAxis.left + $target.outerWidth()];
t_y = [tAxis.top, tAxis.top + $target.outerHeight()];
} else {
// Coordinates and dimensions passed in
t_x = [tAxis.left, tAxis.left + width];
t_y = [tAxis.top, tAxis.top + height];
}
var overlap = false;
$obsticles.each(function () {
var $this = $(this);
var thisPos = $this.position();
var i_x = [thisPos.left, thisPos.left + $this.outerWidth()]
var i_y = [thisPos.top, thisPos.top + $this.outerHeight()];
if (t_x[0] < i_x[1] && t_x[1] > i_x[0] &&
t_y[0] < i_y[1] && t_y[1] > i_y[0]) {
overlap = this;
return false;
}
});
return overlap;
}
I have this loop that produces many elements with different positions:
if ( $row['Type'] == "house") { ?>
<div class="itemW" style="margin-left: <?=$row['X']?>px; margin-top: <?=$row['Y']?>px;">
Item
</div> <?
}
I need to change all the divs left position, I'm trying this:
var items = document.getElementsByClassName("itemW");
items[0].style.left = land.width() / items[0].style.left * 100;
The problem is that items[0].style.left doesn't get the position of the first div. Also I don't know how to do it with all the divs.
You can get all elements with a particular class name with
document.getElementsByClassName("classname");
for anything but IE < 9 at least :P
Then it's just a matter of looping through them like so
var meh = document.getElementsByClassName("classname");
for (var i = 0; i < meh.length; i++)
meh[i].style.left = land.width() / items[0].style.left * 100 + "px"; // "px" is very important.
// also this will only work
// if you have first set the
// element's style attribute.
Try this with jQuery:
$('.itemW').each(function () {
var item = $(this);
var pos = item.position();
item.css('left', (item.width() / pos.left * 100) + 'px');
item.css('top', (item.height() / pos.top * 100) + 'px');
});