This is the third part of the Layout-Adjustment for Nested Tables series, episode one. Today I'm gonna show you how ....
Stop it! Sorry, this is an IT-Blog, not a TV-series. It's different every time :-)
What I did not cover until now was having an ....
To achieve an elastic column in an HTML table, I need to make all cells in that column 100% wide. Additionally I must make sure that all parents of those cells are also 100% wide, up until the top-table. Finally there will be an arbitrary number of columns that contain cells with fixed widths, and one column that shows an elastic behaviour.
Until now the JS code to build a layout-adjuster looked like this:
var categorizer = tableColumnCategorizer();
var columnAdjuster = nestedTablesColumnAdjuster(categorizer);
columnAdjuster.init(tables);
I will now introduce a new module that changes this build-code to the following.
var categorizer = tableColumnCategorizer();
var columnAdjuster = nestedTablesColumnAdjuster(categorizer);
var elasticAdjuster = elasticColumnAdjuster(categorizer, columnAdjuster);
elasticAdjuster.init(tables, elasticColumnCategory);
Use that in the initLayout()
function (see predecessor Blog) for trying out. The tables
and elasticColumnCategory
(e.g. "2.1") variables must be given as parameters. The elasticColumnAdjuster()
function is the new module that makes it possible to define an elastic column. The elastic column will stretch when the table is 100% wide and the browser window widens.
I need to identify all cells that are in the elastic column, or overlap it because they are either span
-cells or parents of elastic cells. Columns are represented by categories, and cells have categories as attributes.
span
-cell overlaps the elastic category. I will call that new function nextCategory(category)
. startsWith(category)
, because "3.2" is a parent of "3.2.1". Let me show the source code and then explain further. I will add to the abstractCategorizer
module, because the new functions logically belong there.
var abstractCategorizer = function(CATEGORY_ATTRIBUTE_NAME)
{
....
var that = {};
/* public functions */
/** @return the next after the given dotted number (category). */
that.nextCategory = function(dottedNumber) {
var head = "";
var tail = dottedNumber;
var lastDotIndex = dottedNumber.lastIndexOf(".");
if (lastDotIndex > 0) {
head = dottedNumber.substring(0, lastDotIndex + 1);
tail = dottedNumber.substring(lastDotIndex + 1);
}
return head+(window.parseInt(tail) + 1);
};
/** @return true when given criterion starts with startPart, considering dots. */
that.startsWith = function(criterion, startPart) {
if (criterion === startPart)
return true; /* equal */
if (criterion.length <= startPart.length)
return false; /* same length but not equal */
var part = criterion.substring(0, startPart.length);
if (part === startPart && criterion.charAt(startPart.length) === '.')
return true; /* excluded "1.22" matching "1.2" */
return false;
};
....
return that;
};
The nextCategory()
function tries to find the rightmost dot in given category, fetch the number behind, increment it and return it with what was to the left of it. When it does not find a dot, it simply increments the given number.
The startsWith()
function compares the given criterion
and the startsWith
part being in question. When they are equal, it returns true. If the criterion
's length is shorter than or equal to the startsWith
's length, it returns false. Finally it is sure that the criterion
is longer than the startsWith
part. Thus a sub-string of it can be compared to the startsWith
part, and when they are equal and the criterion
has a dot after, true is returned. Else it might have been criterion
= "1.2.33" compared to startsWith
= "1.2.3", and that criterion definitely is not parent of "1.2.3", thus false is returned.
I will use these new categorizer-functions in ....
It extends a columnAdjuster
module, in particular, it gets the module it must extend as parameter. That way it does not know whether it works on a real TABLE or a DIV-table, and will be reusable for both. In case TABLE we will pass a nestedTablesColumnAdjuster
module instance to it.
Mind that this is a feature available in script languages only: inheritance at runtime!
In Java, which is a strongly typed compiled language, you could not extend another class at runtime, except when using byte-code-engineering libraries, but these are not part of the language, and the resulting source code is far from being readable.
Here is the new module.
1 | "use strict"; |
I will explain the short functions first, and do the fixNonElasticStretchElastic()
at last, because it is not so easy to understand.
Inheritance is done by assigning the given columnAdjuster
module to the local that
variable. The override is done by replacing the init()
function with a new implementation, after saving the old one to the local variable superInit
.
var elasticColumnAdjuster = function(categorizer, columnAdjuster)
{
....
var that = columnAdjuster;
var superInit = that.init;
that.init = function(elementsToLayout, elasticCategory) {
....
superInit(elementsToLayout),
....
}
return that;
}
The new init()
implementation calls the fixNonElasticStretchElastic()
function with what comes back from the superInit()
call, and the category denoting the elastic column. Then it stretches all tables (received as parameter) to 100%.
The overlapsElastic()
function finds out whether an element overlaps the elastic column. It also receives the span
count of this element, and thus can also cover span
-cells. For that purpose the categorizer.nextCategory()
function is used.
The fixSize()
function is used to fix an element to its current size. This is needed for span
-cells that do not overlap the elastic column. Without fixing them, they also would be elastic.
Finally, the fixNonElasticStretchElastic()
function does what its name announces a little fuzzy. It receives an array of top-level tables to be formatted. Every such table is represented by an object containing the maximumLevel
and the levelArraysMap
, where key is the 0-n nesting-level and value is an array of cells being on that level. For every top-table, the function then separates the cells overlapping the elastic column from those that do not. Only span
-cells are added to the collection of non-elastic cells, and no span
-cells are added to the collection of elastic cells. After that separation, the nonElasticSpanCells
are fixed to their current width, and the elasticNonSpanCells
are stretched to 100%.
Date |
| |||||||||
---|---|---|---|---|---|---|---|---|---|---|
2016-02-05 |
| |||||||||
2016-02-06 |
| |||||||||
2016-02-07 |
|
Mind that when the table was formatted once, and you change the elastic column and format is once more, the non-elastic columns will keep their sizes. Thus the table will get wider and wider. Do a browser page reload in between when trying out different elastic columns.
That's it, hope it works for you. When not, take a look at my homepage, there you'll find the current state of this project. Use Ctl-U to view source, the JS code is at bottom of the page.
ɔ⃝ Fritz Ritzberger, 2016-02-14