I think it misplaced attention to talk exactly what I did to build this site, I have been employed to do this for several years. Read the source if you are interested, otherwise skip.
This particular thing however is a pain and expensive.
A long tale..
A.K.A. discussion on the requirements
Columnisation is a trick from newspapers, it makes it faster/simpler to read dense text. As a layout, I think it is more attractive than just the flat paragraphs. Columnisation ought to be done on the client, the columns need to be set to match the current client configuration. If the browser resizes, we don't want another trip to the server, ideally. To make the columns more readable, I have split the content up into screen-size blocks, so there should be less need to scroll. All the text is currently maximum contrast (black to white), and user can choose the most readable font ~ for them. I looked at adding the pagination on the client side, but this would prove quite expensive. As a discussion on outcomes and desired goals, this is quite informative. The columnisation takes up a noticeable period on some phones. Adding more computation will aggravate the issue.
As the requirements for columnisation are quite common, I did a google search before I wrote any code. This site is implemented using a jQuery module by A Wulf. This module has been published for some time, but has a list of open issues in github. It is mostly stable. A Wulf thinks it is necessary to render the text to find out how large it is, rather than counting characters. My initial plans where less generic (if we assert a row of text takes so many pixels height, we just count the lines and multiply), but configurable. As a generic, A Wulf's solution is clearly technically superior. My approach leads to good user experience, as load times are short. I used A Wulf's solution, as it is already tested.
After deployment, I note that A Wulf's code doesn't like splitting tables. Appendix 1 is a range of text samples, demoing failure and success. The point of failure seems to be individual elements without much text, but large screen space, in the end of the column. My content doesn't have many tables, so I can work round this (and I am already splitting the content on the server, for the pagination). I will create a more systematic solution in due course.
This paragraph is about the site, and nothing for the iceline CMS. The next little UX failure is the numbering. I do have quite a few lists. On the initial columnisation, these get confusing as the numbers keep restarting at 1. I have implemented an extension for the columniser, once using the “start” attribute, and once using CSS counters. My site is HTML5, so “start” is compliant. Other people may not have this option, so the CSS based solution also exists. If you use the second option, you will need to adjust the CSS to match your site. This bit is time expensive. As the CSS counters aren't real elements, they don't automatically get the standard HTML sizing. One needs to manually create it. As these aren't real elements, your normal design tools probably won't work. Please reference the specific style sheet. This style sheet would need to be extended to match your localised branding
Having resolved that, one notices that the indenting on split lists is correct but confusing. That is, if a column splits a sublist; the new list will have all the numbers in same screen area. I think it needs the numbers there to explain the massive left indent, but these need to be clearer. To make this work better, I added CSS to inject elipsis on the split element.Under testing, I added anther condition for correctly numbering lists inside lists, or the code pulls from the last list, not the last list on the same nesting.
Having done that, I took the CSS for closing elipsis out, and wrote it as JS. The CSS depended on A Wulfs library adding a “split” class to the last elements, which it doesn't. This is now in the renumbering functions.
The 'start' attribute doesn't have effect on 'UL' lists in Opera and Chrome, which is reasonable. This does have a effect in Firefox.
I may do performance optimisation on this code later, if the page render time on a phone becomes noticeable. On a PC, I don't notice it.
For the current column, the first list should start at a count so that:
- “zeroth” columns ~ just after a page break, are left untouched;
- When there are non-lists after the last list in the previous column, don't touch;
- With the count from the last list in the previous column;
- If above list wraps into three columns, also include the count from i-2 column;
- If the first list in the current column, contains a sublist, and that sublist is also split, that sublist should be renumbered;
- Do each list type separately, it makes the logic more testable;
Iterate over all the columns..
A Wulfs original module now supports with the following additions:
In the constructor, the options param may include:
- elipsisText ~ the text that gets added to the end of lists, on split items to make it obvious its split. You should set this match whatever you use in the CSS.
- setColumnStart ~ a lambda function that controls what happens when doing a renumber.
The function will be called with 2 parameters:
* $list ~ the element being numbered (in the expected case, its the OL element)
* $int ~ the value it should be set to.
If setColumnStart isn't set, a HTML attribute “start” is added, as $int.
If the computation for $int comes to 0, this function isn't called.
.renumberByJS ($searchTag, $colno, $targetId, $targetClass) where
- $searchTag ~ the tag you wish to renumber. This feature is to apply to mixed documents, not all elements want numbering.
- $colno ~ how many columns the text was split into (this is hidden by a wrapper function in my site). This affects which columns get renumbered.
- $targetId ~ optional, what blocks to renumber. One of these two must be supplied.
- $targetClass ~ optional, what blocks to renumber.
The computation of $int is done via counting child elements, so this should work regardless of what the $searchTag element is.
Hint: If you use the MS platform for development, and are debugging this module (passing the debug flag), you would of course load the JS tools first (press <f12>)?
I had to build this iteratively, as I didn't have a good understanding of the goals at the start. One can't TDD “does it look okay” ~ your testcases would always be out of date. As I am currently operating without a testlab, my release process is clumsier than I would like. Likewise, when the platform most likely to give errors is one you can't install selenuim or behat onto, having test-cases doesn't prove success or failure. Thirdly, as not all the visual affects exist in the DOM, most test libraries can't state success or failure.
This must work cleanly on MSIE8, and MSIE9; due to the business focus of this site.
Despite all the above complications, I achieve success. In the past development would have need to be performed like this, for everything; as tools hadn't been created. The renumbering code started very simple. Then I fixed a lot of edge cases, and its not so simple any more. It passes the test-cases columniser-process, and is available. The development is discussed in the second article on this