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/
Related
----------------------------------------------------
| This is my text inside a div and I want the overf|low of the text to be cut
----------------------------------------------------
Please note that I want the overflow to be removed, so the CSS ellipsis property would not work for me. So basically, I want that the text above to appear like this:
----------------------------------------------------
| This is my text inside a div and I want the overf|
----------------------------------------------------
How is this possible with pure JavaScript?
EDIT
I need a JavaScript function to crop the text because I need to count the characters of the visible text.
Okay, I didn't see the addendum to the question. Although I had previously said it wasn't possible to do this using JavaScript and a font that isn't fixed-width... it actually is possible!
You can wrap each individual character in a <span>, and find the first <span> that is outside the bounds of the parent. Something like:
function countVisibleCharacters(element) {
var text = element.firstChild.nodeValue;
var r = 0;
element.removeChild(element.firstChild);
for(var i = 0; i < text.length; i++) {
var newNode = document.createElement('span');
newNode.appendChild(document.createTextNode(text.charAt(i)));
element.appendChild(newNode);
if(newNode.offsetLeft < element.offsetWidth) {
r++;
}
}
return r;
}
Here's a demo.
You can do this with Javascript. Here is a function that counts the number of visible characters in an element, regardless of external css sheets and inline styles applied to the element. I've only tested it in Chrome, but I think it is cross browser friendly:
function count_visible(el){
var padding, em, numc;
var text = el.firstChild.data;
var max = el.clientWidth;
var tmp = document.createElement('span');
var node = document.createTextNode();
tmp.appendChild(node);
document.body.appendChild(tmp);
if(getComputedStyle)
tmp.style.cssText = getComputedStyle(el, null).cssText;
else if(el.currentStyle)
tmp.style.cssText = el.currentStyle.cssText;
tmp.style.position = 'absolute';
tmp.style.overflow = 'visible';
tmp.style.width = 'auto';
// Estimate number of characters that can fit.
padding = tmp.style.padding;
tmp.style.padding = '0';
tmp.innerHTML = 'M';
el.parentNode.appendChild(tmp);
em = tmp.clientWidth;
tmp.style.padding = padding;
numc = Math.floor(max/em);
var width = tmp.clientWidth;
// Only one of the following loops will iterate more than one time
// Depending on if we overestimated or underestimated.
// Add characters until we reach overflow width
while(width < max && numc <= text.length){
node.nodeValue = text.substring(0, ++numc);
width = tmp.clientWidth;
}
// Remove characters until we no longer have overflow
while(width > max && numc){
node.nodeValue = text.substring(0, --numc);
width = tmp.clientWidth;
}
// Remove temporary div
document.body.removeChild(tmp);
return numc;
}
JSFiddle Example
You're trying to force a CSS problem into JavaScript. Put the hammer away and get out a screwdriver. http://en.wiktionary.org/wiki/if_all_you_have_is_a_hammer,_everything_looks_like_a_nail
Solving the answer of character count is probably irrelevant if you take a step back. The last character could be only partially visible, and character count is drastically different given font size changes, the difference of width between W an i, etc. Probably the div's width is more important than the character count in the true problem.
If you're still stuck on figuring out the characters visible, put a span inside the div around the text, use the css provided in other answers to this question, and then in JavaScript trim one character at a time off the string until the span's width is less than the div's width. And then watch as your browser freezes for a few seconds every time you do that to a big paragraph.
try this, it requires a fixed width if that is ok with you: http://jsfiddle.net/timrpeterson/qvZKw/20/
HTML:
<div class="col">This is my text inside a div and I want the overf|low of the text to be cut</div>
CSS:
.col {
width:120px;
overflow: hidden;
white-space:nowrap;
}
.col { width:40px; overflow: hidden; white-space:nowrap; }
White-space: nowrap; is needed when the content has spaces.
Either way, long words in single lines do not appear. http://jsfiddle.net/h6Bhb/
I have a table cell in which other tables are displayed (with background colors). The inner tables aren't always shown; they can be hidden via a button (change class with jQuery). Now I want the outer cell to always be filled with color. That means if only one table is displayed, its width should be 100%. When two are displayed, each width should be 50%, and so on.
How am I supposed to solve this?
Here's an example:
...
<td>
<table class="show"><tr><td></td></tr></table>
<table class=""><tr><td></td></tr></table>
<table class="show"><tr><td></td></tr></table>
</td>
...
In this case, the width should be 50%
You can change the width value with Jquery.
var count_table = $(".show").length; // count ".show" elements
$(".show").each(function{ // iteration on each ".show"
var width = 100/count_table; // % value of new width
$(this).css("width", width+"%"); // CSS modification
});
This code is just adapted for one TD element. You need to iterate on each "td" too.
(I hope I answered your problem)
To change the width of your elements you can use jquery.
Here's the page explaining how.
Here is another way: http://jsfiddle.net/ypJDz/1
A bit too complicated for what you need, but a great deal of possibility to expand.
function resizeTables() {
var baby = $("td > table.show"),
amount = baby.length,
mother = $("body");
baby.width(function () {
var w = mother.width() / amount;
return w;
});
}
resizeTables();
$("button").click(function () {
var $this = $(this),
ID = $this.index() + 1;
$("td > table:nth-child(" + ID + ")").toggleClass("show");
resizeTables();
});
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")
});
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.