This is the last of my series about adjusting columns of nested tables. It answers the question
-
how can all of the
already implemented
adjustment be applied to
DIV
tables?
Tables can be built not only by HTML TABLE
elements,
but also by DIV
elements when using specific CSS display
styles.
Here is an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type="text/css"> .table { display: table; } .thead { display: table-header-group; } .tbody { display: table-row-group; } .tr { display: table-row; } .td, .th { display: table-cell; } </style> </head> <body> <div class="table"> <div class="thead"> <div class="tr"> <div class="th">Column 1</div> <div class="th">Column 2</div> </div> </div> <div class="tbody"> <div class="tr"> <div class="td">Content One</div> <div class="td">Content Two</div> </div> <div class="tr"> <div class="td">Content Three</div> <div class="td">Content Four</div> </div> </div> </div> </body> </html> |
This displays like a normal HTML TABLE
.
The only difference is that DIV
tables do not support certain features like e.g. the colspan
attribute,
and do not have browser-defaults for padding
, margin
etc.
How can I reuse the JS code for TABLE
elements to do the same for
DIV
tables?
By implementing new categorizer and adjuster modules, both extending their already provided abstractions.
As you may remember, I implemented modules with different responsibilities:
- identifying column cells as abstractCategorizer
- concrete implementation as tableColumnCategorizer
- measuring and sizing columns as abstractLayoutAdjuster
- concrete implementation as nestedTablesColumnAdjuster
They were finally built together to create the adjuster fitting to the requirements.
Now here is the new categorizer module implementation for DIV
tables:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | "use strict"; /** * Concrete categorizer for HTML div table columns. */ var divTableColumnCategorizer = function(CATEGORY_ATTRIBUTE_NAME) { var that = abstractCategorizer(CATEGORY_ATTRIBUTE_NAME); /** @return always 1 because DIVs do not support the colspan attribute. */ that.getSpan = function(element) { return 1; }; /** * @return true when given element should be categorized. * This implementation returns true when element is * DIV and its "display" CSS property is "table-cell". */ that.isElementToCategorize = function(element) { if ( ! element.tagName === "DIV" ) return false; var style = window.getComputedStyle(element); return style["display"] === "table-cell"; }; return that; }; |
This module extends abstractCategorizer
.
It then implements getSpan()
to return 1 (does not support colspan
),
and isElementToCategorize()
to identify table-cells of DIV
tables.
Now I need to write an adjuster that can find nested DIV
tables.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | "use strict"; /** * Concrete implementation of a layout-adjuster for HTML DIV-tables. * @param abstractLayoutAdjuster the layouter to extend. */ var nestedDivTablesColumnAdjuster = function(categorizer, abstractLayoutAdjuster) { var findNestedDivTables = function(element, nestedDivTables) { nestedDivTables = (nestedDivTables !== undefined) ? nestedDivTables : []; var children = element.children; for (var i = 0; i < children.length; i++) { var child = children[i]; if (child.tagName === "DIV") { var style = window.getComputedStyle(child); if (style["display"] === "table") nestedDivTables.push(child); } findNestedDivTables(child, nestedDivTables); } return nestedDivTables; }; var that = nestedTablesColumnAdjuster(categorizer, abstractLayoutAdjuster); /** @return all nested DIV table elements below given one. */ that.getNestedContainers = function(elementToLayout) { return findNestedDivTables(elementToLayout); }; return that; }; |
This module extends its predecessor module nestedTablesColumnAdjuster
.
It overwrites the getNestedContainers()
function to call the private findNestedDivTables()
implementation. Unfortunately the JS built-in querySelector()
function can not be used here, because
there is no CSS selector that can target CSS styles like display: table
. So we need to loop
through children and read their styles.
Here comes the code to integrate the two new modules:
var categorizer = divTableColumnCategorizer(); var abstractAdjuster = abstractLayoutAdjuster(categorizer); var columnAdjuster = nestedDivTablesColumnAdjuster(categorizer, abstractAdjuster); columnAdjuster.init(tables);
Remember that I provided an abstract module that provides pre-defined columns widths.
To be able to integrate that module into the build-chain, I needed to specify
the abstractAdjuster
parameter separately.
Of course also both the elastic-column and predefined-column-widths modules can be reused for DIV
tables.
Just the build chain changes:
var categorizer = divTableColumnCategorizer(); var abstractAdjuster = predefinedSizes ? abstractPredefinedSizeAdjuster(categorizer, predefinedSizes) : abstractLayoutAdjuster(categorizer); var adjuster = nestedDivTablesColumnAdjuster(categorizer, abstractAdjuster); var columnAdjuster = elasticColumn ? elasticColumnAdjuster(categorizer, adjuster, elasticColumn) : adjuster; columnAdjuster.init(tables);
That's all! You can go to my homepage to see this working.
The beauty of code reuse by inheritance is that you are mostly done by overwriting only a few things. All object-oriented languages provide inheritance. Although JS is not object-oriented, similar can be provided by using functional inheritance.
Generally JS provides
several kinds of inheritance
(would we have needed so much?). There are prototypal, classical, .... I wrote about this in one of my
past Blogs.
I recommend
functional inheritance,
because it is easy to use and understand, and
avoids the risks of the prototype
chain and the this
pointer,
and does not require understanding the new
operator. And private instance variables work fine.
Keep it simple, and you will win!
Keine Kommentare:
Kommentar veröffentlichen