Fix my random image positioning script so the images don't overlap - javascript

I made a script (here's a fiddle that builds upon my JS) for random positioning of <li> elements.
The elements overlap each other, though.
How can i modify my code so the items don't overlap anymore?
This is the script:
var images = [];
function init() {
$('.friend-selection li > div').each(function(){
var id = this.id;
var img = $('#img_' + id);
var randomTop = 500*Math.random(); //random top position
var randomLeft = 500*Math.random(); //random left position
$("#parent_" + id).css({ //apply the position to parent divs
top : randomTop,
left : randomLeft
});
});
};
init();
I have this html. A ul with li items:
<li id="parent_picture1">
<div id="picture1">
<div class="glow"></div>
<img src="static/img/voorbeeld/vrouw.jpg" width="111" height="166" id="img_picture1">
<span class="name">Naam Achternaam</span>
</div>
</li>

I'd need to know a bit more about the problem to decide how I'd do this, but here are some ideas that may work.
If the images all have the same size, you should be able to do something using modulus, e.g. randomTop = (500 * Math.random()) % picWidth.
Of course you'd need to keep track of which slots you've already used, so if the pictures array is going to be dense I'd probably add the slots to a random array and iterate through it.
var slots = [];
for (var y = 0; y < 5; y++)
{
for (var x = 0; x < 5; x++)
{
slots.push([x, y]);
}
}
slots.sort(function () { return (0.5 - Math.random()); });
$('.friend-selection li > div').each(function() { ... }
(Note that the random method I used isn't that great.)
If the pictures aren't of a fixed size, things get more complicated. I'd start by taking a look at jQuery Masonry for code, ideas or even a complete solution.

You need to save the coordinates of each random position in an array.
For each <li>:
Get a random coordinate pair random_coordinates.
Check random_coordinates against each pair in positions_array:
does random_coordinates[0] + the width of your <li> overlap the positions_array[i][0] + the width of your <li> range anywhere?
If yes, start over.
does random_coordinates[1] + the height of your <li> overlap the positions_array[i][1] + the height of your <li> range anywhere?
If yes, start over.
If no collisions have been detected, push the random_coordinates pair on positions_array, apply the css, proceed with the next <li> item.

Related

CSS/JS - Can you treat browser auto line breaks like rows to color them?

I am not quite sure how to put my Question in the right words, but I try to describe what I want to do.
Let's say we have a set of data (eg. numbers) from a database and they need to be output as a list. I put a predefined amount of data-fields in a row (for dekstop systems). Every data is in a SPAN with color/margin/padding styles. (Like table-cells.) After that amount of Spans is reached, a forced line break is given. And with each line break, the background color for all spans in that "row" is altered (odd/even). So far no problem.
However, if now someone checks that page with a Smartphone (or you simple resize your Browser Window), the predefined amount does not fit anmyore. As example, in large resolutions you have 6 Spans side by side, colored odd, than 6 Spans colored even. In a small resolution you maybe only have 3 Spans side by side, however in my design you have still 6 Spans colored odd, so two "rows" with the same background-color before it is altered.
Example HTML Output:
<span class="odd">Number 01</span>
<span class="odd">Number 02</span>
<span class="odd">Number 03</span>
<span class="odd">Number 04</span>
<span class="odd">Number 05</span>
<span class="odd">Number 06</span>
<br/>
<span class="even">Number 07</span>
<span class="even">Number 08</span>
<span class="even">Number 09</span>
<span class="even">Number 10</span>
<span class="even">Number 11</span>
<span class="even">Number 12</span>
<br/>
I have absolutely no idea if it is possile to get to know - maybe through Javascript or CSS, how many spans are displayed in a "row", to automate the odd-even-coloring, make it in a way responsive.
Check my Fiddle to maybe show better what I'm trying to get.
Don't you want to use any css framework? Like Bootstrap or Foundation? I hope that will make your work more easier.
Please go through the link.
https://getbootstrap.com/examples/grid/
It already has the solution for mobile device and medium screen.
I can't think of any way to pull it off with CSS, but here's a javascript solution. Browser support is a little off. Didn't realize you tagged jQuery in your post, but it should work on modern browsers at least:
window.addEventListener('resize', function(){
var cells = document.querySelectorAll('.odd,.even');
var activeClass='even', activeLine = 0;
for(var i = 0, len = cells.length; i < len; i++) {
cells[i].classList.remove('odd');
cells[i].classList.remove('even');
if(activeLine != cells[i].offsetTop + cells[i].offsetHeight) {
activeClass = (activeClass === 'even') ? 'odd' : 'even';
activeLine = cells[i].offsetTop + cells[i].offsetHeight
}
cells[i].classList.add(activeClass);
}
});
/* edit: forgot to dispatch the event.
jQuery makes this so much easier to write
than the monstrosity below. */
var event;
event = document.createEvent("HTMLEvents");
event.initEvent("resize", true, true);
window.dispatchEvent(event);
Demo here: http://jsfiddle.net/xh0o6gvy/1
Edit: Here is the jQuery version that Sunny put together found in a fiddle from the comment below. It's definitely a better way to go than the above code if compatibility with older browser versions is required.
function colorSpans() {
var containers = $('.span_container');
containers.each(function() {
var activeClass = 'even';
var activeLine = 0;
var cells = $(this).children('.odd, .even');
cells.each(function() {
$(this).removeClass('odd even');
var offset = $(this).offset();
var height = $(this).outerHeight();
if(activeLine != offset.top + height) {
activeClass = (activeClass === 'even') ? 'odd' : 'even';
activeLine = offset.top + height;
}
$(this).addClass(activeClass);
});
});
}
$(window).on('resize', function() {
colorSpans();
});
colorSpans();
Thanks everybody for your help and inspirations!
So just to round it up, here is the final function I am now using that works with fixed and variable container sizes and recolors container by container.
function colorSpans() {
var containers = $('.span_container');
containers.each(function() {
var activeClass = 'even';
var activeLine = 0;
var cells = $(this).children('.odd, .even');
cells.each(function() {
$(this).removeClass('odd even');
var offset = $(this).offset();
var height = $(this).outerHeight();
if(activeLine != offset.top + height) {
activeClass = (activeClass === 'even') ? 'odd' : 'even';
activeLine = offset.top + height;
}
$(this).addClass(activeClass);
});
});
}
An here is the fiddle: http://jsfiddle.net/z9db7p0t/1/
You could accomplish this with Javascript or plain CSS depending on the rest of your HTML/CSS.
Are the width of the span elements fixed? What about their parent container? If so, you should know where the breakpoints in your layout exist and easily target the elements via css within the proper media queries.
If the size is dynamic, you could update the classes on the spans by calculating how many spans could fit in a row. This would need to be called each time the page was resized however. Using jQuery:
$(function() {
var container = $('#span-container'),
spans = container.find('span');
$(window).on('resize', function(evt) {
var containerWidth = container.width(),
spanWidth = spans.width();
var howManyPerRow = Math.floor(containerWidth / spanWidth);
//reset rows
spans.removeClass('odd even');
var row = 'odd',
c = 1;
spans.each(function() {
$(this).addClass( row );
if( c % howManyPerRow == 0 ) {
row = (row == 'odd') ? 'even' : 'odd';
}
c++;
});
});
});
You are going to need to remove the <br /> tags for this to work correctly. Also, you should set white-space: nowrap; on the spans IMO.

How to find width of inner elements

I couldn't find any other questions asking the same thing, though that may be a problem with my search phrasing.
I'm trying to figure out how to find the largest width of all elements contained inside of a container div with a fixed width. So, in the image below, the black box is the container div with a fixed width. The red box represents the contents of the container div, which are subject to change. I want to find the width of the red box, using only the black box in js.
Here is a jsfiddle with what I've been working on/trying:
http://jsfiddle.net/w87k5/1/
the current jquery functions I've tried, with no success:
.width();
.innerWidth();
.outerWidth();
.scrollLeft();
Note: I do not know ANYTHING about the contents of this container. They could be any html element or mix of html elements, from divs to imgs to iframes. I could put a "red box" without a fixed width surrounding them. Overflow of the black box will be hidden.
Update: There could be any number of children in the container. Like this: http://jsfiddle.net/w87k5/3/
Update 2: I'm going to run benchmark speed tests on all of the answers to see which one is the fastest, and select one after that. Thanks for all your input!
Benchmarks:
I generated 1000 divs with a random width of inbetween 0 and 100, recorded the Date().getTime(), did the test, then recorded time again. Here are the results:
~2418 avg. milliseconds with the for loop for length. I might have messed this one up somehow?
for (var i = 1; i <= count; i++){
var q = $("#box :nth-child(" + i + ")").width();
if(q > box){
box = q;
}
}
~34.4 avg. ms for the .each loop.
~22.4 avg. ms for the .map function. (Chosen answer.)
If you need all nested elements can search with * selector which will return all descendent elements:
var widthArray=$('#box *').map(function(){
return $(this).outerWidth();
}).get();
var maxWIdth= Math.max.apply(Math, widthArray);
For just children:
var widthArray=$('#box').children().map(function(){....
You could use .each() to cycle though each child element.
jsFiddle example
var widths = [];
$('#box').children().each(function(i){
widths[i] = $(this).width();
});
alert(Math.max.apply(Math, widths));
Which will return the width of the child element with the largest width.
Get the number of children, and loop through to get the width of each
$(document).ready(function () {
var count = $("#box").children().length;
var h = 0;
for (var i = 1; i <= count; i++) {
max = $("#box :nth-child(" + i + ")").width();
var h = Math.max(max, h);
}
alert(h);
});
http://jsfiddle.net/JDVN3/1/
Please not that the index starts from 1 and not 0.
Check out: http://api.jquery.com/nth-child-selector/

jquery animate ul by list item

I have a ul that I want to center vertically to the vertical center of another element based upon the li that a user clicks. I have some code worked up here:
http://jsfiddle.net/XxmAT/1/
But it is unfortunately not behaving how I would like it to, I know my math is off, so any help would be much appreciated!
I think this one is prettier:
http://jsfiddle.net/XxmAT/33/
Usually it is best to think of the logic first, and start making variables after. Because of the cluster of variables, it was hard to understand what you meant with all the code. The click function just got a lot simpeler, and it is easier to understand what I am subtracting from which number, to get the desired result...
Sidenote: The - 180 at the end is probably the strange (and enormous) margin.
here you go. Got the height of the li and multiplied it to account for the height of the space between li and then multiplied by the index that was clicked.
http://jsfiddle.net/XxmAT/36/
var u_menu = $("#menu-main-menu").position().top;
var z_menu = $("#menu-main-menu").height();
var y = $("#box1").offset().top;
var z = $('#box1').height();
var c_menu = (z_menu / 2) ;
$("#menu-main-menu").css('top', - c_menu);
$('ul#menu-main-menu li').click(function (e) {
// this is the dom element clicked
var index = $('li').index(this);
var x = $('li').height() * 2;
var offset_li = (u_menu + y) / index ;
$('ul#menu-main-menu').animate({top: -(x*index)}, "slow")
});​

Equalize two ULs by height

I have a case where I'm provided with one UL, and an unknown number of LIs. I'm looking to use JS to split the LIs into 2 ULs (I'm using jQuery). I'm able to split the ULs evenly by number of LIs, but I'd like to split it based on the height of each LI as well, so that both ULs are close to the same height.
Any help with this would be appreciated, I don't feel like I'm getting anywhere with the approach I started with.
Thanks.
EDIT: JS code I currently have. The HTML is just a straight UL/LI, each LI can be of varying height.
var $sections = $('div.subsection');
$sections.each(function(){
var $section = $(this);
var $list = $section.children('ul');
var $items = $list.children('li');
var itemCount = $items.size();
var leftover = itemCount % 2;
var itemsPerColumn = Math.floor(itemCount / 2);
var $newList = $('<ul />');
$items.each(function(){
var $this = $(this);
var index = $items.index($this);
if (index >= (itemsPerColumn + leftover)) {
$this.remove().appendTo($newList);
}
});
$list.after($newList);
_equalizeListHeights();
function _equalizeListHeights(){
var listHeight = $list.height();
var newListHeight = $newList.height();
if (listHeight > newListHeight){
var $lastItem = $list.children('li:last');
var lastItemHeight = $lastItem.height();
if (listHeight - lastItemHeight > newListHeight + lastItemHeight){
$lastItem.remove().prependTo($newList);
_equalizeListHeights();
}
}
}
});
You can do it via CSS:
.double_column_list li {float: left; width: 50%;}
<ul class="double_column_list">
<li>Awesome Stuff Awesome Stuff Awesome Stuff Awesome Stuff Awesome Stuff</li>
<li>Awesome Stuff</li>
<li>Awesome Stuff</li>
<li>Awesome Stuff</li>
</ul>
To get 3 column, set width: 33.333%, 4 column width: 25% and so on.
Of course, if you keep increasing the height of one li to a point where rest of the lis can't match, this would look bad. But then, that issue cannot be fixed through JS either.
http://jsfiddle.net/rQJQb/
Update:
As pointed out by commenters, if list items are not sorted by height (i.e. height of any one list item in the middle may be bigger/smaller than the ones preceding it), a sorting is needed: http://jsfiddle.net/rQJQb/2/
I think I can at least see the approach here:
Calculate the total height of all the list items (total), and store all the individual heights
Calculate the height of one list (total / 2)
Determine an algorithm to sum a set of heights to come as as possible to total / 2, without exceeding it.
Put the elements with these heights into the first list, and put the rest into the second
Step 3 is the tricky bit. It's related to the Subset Sum Problem.
EDIT
Here's a brute-force algorithm which solves your problem. It doesn't run on window.resize, because that would be silly. If you want to see it change, resize the result window, then push run.
//Sum a jQuery wrapped array
$.fn.sum = function() {
var total = 0;
this.each(function() { total += this; });
return total;
};
//Mask elements with a bitmask
$.fn.mask = function(mask) {
return this.filter(function(i) {
return (mask >> i) & 1;
})
}
//Get the sizes, and sneakily return a jQuery object
var sizes = $('.first li').map(function() { return $(this).outerHeight() });
var total = sizes.sum();
var maxTotal = total / 2;
var best = {
total: 0,
mask: 0
}
for (var subsetMask = 1; subsetMask < (1 << sizes.length); subsetMask++) {
//Sum all the heights in this subset
var subsetTotal = sizes.mask(subsetMask).sum();
//New optimal solution?
if (subsetTotal > best.total && subsetTotal <= maxTotal) {
best = {
total: subsetTotal,
mask: subsetMask
};
}
}
$('.first li').mask(best.mask).appendTo('.second');
The CS problem you are trying to solve
You should look into the Backpack Problem. The items to be 'inserted into the backpack' will be the LIs. Each LI will have a weight and value equivalent to its height. The backpack capacity should be half the sum of all the LI heights. Run an algorithm to solve the backpack problem and your LIs will be divided into two sets with heights as you've described.
Intuitive explanation
The backpack algorithm finds a subset of items such that the value is as large as possible, but the weight doesn't exceed the backpack capacity. But our weight and value of each LI is its height, and the backpack capacity is half the total height of all LIs combined.
So essentially what it will give us is a set of all LIs such that the height is as high as possible without exceeding 1/2 the total height. In the case where you should end up with two equal-height sets of LIs, this will be one of the sets. In the case where you should end up with two sets of LIs with different heights, the backpack problem solution will return the set with a smaller height (and the remaining, unchosen LIs would be the second set).
Solution
Try the code used here: http://rosettacode.org/wiki/Knapsack_problem/Bounded#JavaScript
Or if you want to roll your own (not recommended - why bother?): http://en.wikipedia.org/wiki/Knapsack_problem#Unbounded_knapsack_problem
$("ul.first li").each(function() {
if ($("ul.first").outerHeight() > $("ul.second").outerHeight()) {
$(this).appendTo($("ul.second"));
}
});
Use this jQuery code.

How can I find the position of an HTML element relative to the entire desktop screen using JavaScript?

How do i find the X,Y coordinates of a particular html element ( eg. div, table, lable, etc...) relative to the desktop screen (i.e. outside the browser window) using JavaScript?
I can find the height and width of the element by using offsetHeight and offsetWidth, but can't find anything that can give me exact X,Y coordinate of the element relative to the user’s entire desktop screen.
I think you have to follow the tree up, through the parents, and keep adding the offsets, like described here:
http://bytes.com/topic/javascript/answers/90547-how-get-absolute-position-element
function getY( oElement )
{
var iReturnValue = 0;
while( oElement != null ) {
iReturnValue += oElement.offsetTop;
oElement = oElement.offsetParent;
}
return iReturnValue;
}
I don't think it is not possible even it is quite simple.
Find the position of browser relative to screen
Find the element position relative to view port.
Add the coordinates and you will have the output you want
Follow the piece of code I written:
var element = document.getElementById("ID of the element");// you can use any method to find the element ..
var position = getPosition(element);
function getPositions(obj)
{
var p = [];
var position = obj.getBoundingClientRect();
p[0] = window.screenX + position.left;
p[1] = window.screenY + position.top;
p[2] = position.width;
p[3] = position.height;
return p;
}

Categories