This is some exercise code on the problem of populating a <ul>
element subject to constraints and other criteria. It uses an "out-of-sight" dummy list to compute the dimensions it needs to lay out the actual list.
xxxxxxxxxx
<html>
<meta charset="utf-8">
<title>populating a list</title>
<style>
*{
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html, body, div, ol, ul, li, br, img, hr{
margin:0;
padding:0;
border:0;
outline:0;
}
*{
/* font-family: "Gill Sans", "Gill Sans MT", sans-serif; */
font-family: Consolas, Monaco, "Andale Mono WT", "Andale Mono", "Nimbus Mono L", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Courier New", Courier, monospace;
}
.list-container{
cursor:default;
}
.list-container li{
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
.list-container ul{
list-style:none;
}
.list-container li{
-webkit-background-clip: padding-box;
background-clip: padding-box;
}
.list-container li{
border-style:solid;
border-color:rgb(255, 255, 255);
border-color:rgba(0, 0, 0, 0);
}
.list-container li:hover{
font-weight:bold;
border-color:rgb(0, 0, 0);
border-color:rgba(0, 0, 0, 1);
}
/* TBD = to be determined (dynamically, that is) */
/*
#the-list ul, #the-list ol{
width: TBD;
}
#the-list li{
width: TBD;
}
*/
.list-container li{
float: left;
}
.list-container br:last-child{
clear: left;
}
.list-container {
margin-bottom: 1em;
}
#out-of-sight {
position:relative;
}
#out-of-sight .list-container{
left:-999999px;
position:absolute;
}
</style>
<body>
<button>next</button>
<div id="main">
<div id="the-list" class="list-container">
<ul></ul>
<br/>
</div>
</div>
<div id="out-of-sight">
<div id="dummy-list" class="list-container">
<ul></ul>
<br/>
</div>
</div>
<script src="//code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var lists = [
"<00>+<01>+<02>+<03>+<04>+<05>+<06>+<07>+<08>+<09>+<10>+<11>+<12>",
"entity-00+entity-01+entity-02+entity-03+entity-04+entity-05+ent" +
"ity-06+entity-07+entity-08+entity-09+entity-10+entity-11+enti" +
"ty-12",
"item-00+item-01+item-02+item-03+item-04+item-05+item-06+item-07" +
"+item-08+item-09+item-10+item-11+item-12",
"Aruba+China+Iraq+Peru+Benin+Cuba+Mali+Togo+Chad+Fiji+Niue+Chile" +
"+Guam+Oman",
"Canada+India+Niue+Dominica+Kazakhstan+Slovenia+Ghana+Kyrgyzstan" +
"+Sri Lanka+Grenada+Morocco+Thailand+Guinea+Namibia",
"Aruba+Mayotte+Bermuda+Mexico+Czech Republic+Morocco+Egypt+Qatar" +
"+Estonia+Trinidad and Tobago+Gibraltar",
"Benin+Chile+Monaco+Montenegro+Niger+Tajikistan+Tokelau+Tunisia+" +
"Ukraine+United States Minor Outlying Islands+Yemen"
].map(split_pre_list),
loremipsum = split_pre_list(
"01. Lorem+02. ipsum+03. dolor+04. sit+05. amet+06. consectetur+07. adipiscing+08. elit+09. Nunc+10. accumsan+11. nec+12. tellus+13. eleifend+14. pellentesque+15. Donec+16. eu+17. posuere+18. massa+19. Vestibulum+20. ut+21. dui+22. a+23. mauris+24. imperdiet+25. vulputate+26. ac+27. vitae+28. sem+29. Cum+30. sociis+31. natoque+32. penatibus+33. et+34. magnis+35. dis+36. parturient+37. montes+38. nascetur+39. ridiculus+40. mus+41. Proin+42. leo+43. quam+44. pellentesque+45. id+46. convallis+47. sed+48. tempus+49. ac+50. lorem+51. Donec+52. accumsan+53. sem+54. nec+55. eros+56. aliquet+57. egestas+58. Aenean+59. elementum+60. aliquet+61. rhoncus+62. Integer+63. tristique+64. varius+65. nulla+66. vitae+67. suscipit"),
current_index = 0,
sentinel = String.fromCharCode(29);
d3.range(1, loremipsum.length + 1)
.forEach(function (i) { lists.push(loremipsum.slice(0, i)); });
$('button').click(function () {
populate_list(lists[current_index]);
current_index = ((current_index + 1) % lists.length);
}).click();
function populate_list (data) {
_populate_list_0(d3.select('#out-of-sight ul'), data);
function get_width (t) {
return t.getBoundingClientRect().width
}
var all_widths = d3.select('#out-of-sight')
.selectAll('li')
.filter(function () {
return d3.select(this).style('display') !== 'none';
})[0]
.map(get_width)
.sort(d3.ascending),
unpadded_colwidth = Math.round(d3.quantile(all_widths, 0.9)),
hpadding = 10,
borderwidth = 1,
colwidth = unpadded_colwidth + (2 * borderwidth) + hpadding,
hmargin = 10,
min_rows = 4,
max_ncols = ~~((data.length - 1)/(min_rows - 1)),
max_width = 450,
ncols = Math.max(1, Math.min(max_ncols, ~~(max_width/(colwidth + hmargin))));
// console.log(max_ncols, ~~(max_width/(colwidth + hmargin)), ncols);
_populate_list('the-list', data, ncols, colwidth, hpadding, hmargin, borderwidth);
}
function _populate_list_0(list, data) {
var lis0 = list.selectAll('li'),
lis = lis0.data(data);
enter = lis.enter(),
exit = lis.exit();
exit.style('display', 'none');
enter.append('li');
lis.text(String)
.style('display', '')
.style('visibility',
function (d) { return d === sentinel ? 'hidden' : 'visible'; });
}
function _populate_list(which, data, ncols, colwidth, hpadding, hmargin, borderwidth) {
var ul = d3.select('#' + which + ' ul');
_populate_list_0(ul, d3.merge(columnate(data, ncols)));
// _populate_list_0(ul, data);
ul.style('width', (ncols * (colwidth + hmargin)) + 'px');
ul.selectAll('li')
.style('width', colwidth + 'px')
.style('border-width', borderwidth + 'px')
.style('padding', '0 ' + (hpadding/2) + 'px')
.style('margin', '0 ' + (hmargin/2) + 'px');
}
function columnate (array, ncols) {
var nrows = Math.ceil(array.length/ncols);
return d3.transpose(chunk(pad_array(array, nrows * ncols), nrows));
}
function pad_array (array, n) {
return array.concat(d3.range(n - array.length)
.map(function () { return sentinel; }));
}
function chunk (array, chunksize) {
return d3.range(array.length/chunksize)
.map(function (i) {
var s = i * chunksize;
return array.slice(s, s + chunksize);
});
}
function split_pre_list (array) {
return array.split('+').sort();
}
</script>
</body>
https://code.jquery.com/jquery-1.10.2.min.js
https://d3js.org/d3.v3.min.js