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")
});
Related
I'm creating a <table> element in the DOM and using javascript to dynamically append many cells to it. For the sake of explanation let's say I create 10 rows with 10 fields per row. I'm using simple counters to assign unique IDs for the div containers inside of those fields. Easy enough. This is what I get:
<table>
<tr><td><div id="field0"><div id="handle0"></div></div></td></tr>
.....
<tr><td><div id="field99></div id="handle99"></div></div></td></tr>
</table>
Note that the numbers 0-99 are what is dynamically appended to each element ID.
I now want to go ahead and attach the jQueryUI .draggable function to each handle and retrieve the coordinates of each handle relative to each surrounding parent div like so:
for (var counter = 0; counter < 100; counter++) {
var dragHandle = $('#handle' + counter);
var dragField = $('#field' + counter);
dragHandle.draggable({
containment: dragField,
scroll: false,
drag: function () {
var offset = $(this).offset();
var xPos = (offset.left) - $(this).offsetParent().offset().left;
var yPos = (offset.top) - $(this).offsetParent().offset().top;
console.log(xPos);
console.log(yPos); // These add up?!
}
});
}
Now, the functions work, the table gets properly initialized and all of the individual handles in the table are now draggable.
The problem is that the xPos and yPos values that are returned by the above function are not the correct coordinates relative to each field but instead they add up.
I feel like I'm missing something terribly obvious and would really appreciate if someone could help.
Edit: The example above uses console.log for simplification. My original script performs more complex computations in the on drag event. I won't be able to use a class selector to go through all of the elements like someone suggested in the comments because I need to retrieve unique offset and position values for each unique handle ID relative to its unique containment ID.
var xPos=(offset.left)-$(this).position( ).left
var yPos=(offset.top)-$(this).position( ).top
Instead of offsetParent you can modify.
var xPos = (offset.left) - $(this).parent().offset().left;
var yPos = (offset.top) - $(this).parent().offset().top;
I'm trying to create an element(ul) that the width can be changed depending on the number of element(li) contained in this (ul).
I used jquery to get this done but i don't get the right result as i'm making an error and i don't how to fix it.
here is my jquery code:
jQuery('ul.class').each(function(){
var n = jQuery('ul.class li').length;
console.log(n);
jQuery(this).css('width',n*(jQuery('ul.class li').width() + 10));
console.log(n*jQuery('ul.class li').width());
});
10 in this code is the border or each element.
The issue is in this part var n = jQuery('ul.class li').length and i don't know of to get the exact count of each li contained in a ul.
li has a width 150px
jQuery('ul.class').each(function(){
var n = jQuery(this).find('li').length;
alert(n);
jQuery(this).css('width',n*(jQuery(this).find('li').width() + 10));
alert(n*jQuery(this).find('li').width());
});
Demo:
http://jsfiddle.net/84Bv6/1/
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/
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.
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.