This page looks best with JavaScript enabled

Gadget - Cargo pagevalues improvement

 ·  ☕ 6 min read

For my third gadget tools post I’m going to talk about a somewhat “creative” approach I took to improving the layout of the Cargo pagevalues view.

(Note: All code in this post was originally written by me for Leaguepedia and is licensed under CC BY-SA 3.0.)

Motivation

I spend a lot of time looking at ?action=pagevalues pages on Leaguepedia. They’re crucial for debugging Cargo issues. The very first question when a query isn’t working is, “What data is actually being stored to the table?” and the pagevalues tab is usually the easiest way to find this out. But when I’m dealing with pages that write 400 rows across 7 different tables (this is not an exaggeration), and many of these tables have 20+ columns, it’s pretty easy to get lost.

Screenshot of a table with no context in the middle of a pagevalues tab

Especially when pressing Ctrl+F and jumping from table to table, often I had no idea what table I’m currently looking at. I wanted to improve this state of affairs, and just adding a label to each (wiki) table showing the name of the Cargo table represented was really all I needed. There were a couple rules:

  • Whatever I did could not modify the DOM in any way that added nodes, because such a modification could cause a vertical “jump” in the page after content initially finished loading, which was unacceptable to me
  • No API calls, I’m only allowed to use information already loaded onto the page
  • No modifying Cargo source code, this has to be purely a CSS/JS “hack” that I can deploy in user CSS/JS

(These rules are pretty standard with most UI modification I do, I’d say the second is the one that gets relaxed the most depending on context, especially when it’s a tool just for myself.)

Much later I added a second modification that I’m throwing in here too, which was just to float and sticky the table of contents on the right side of the page, so that it always shows on the page.

Here you can see the final result of these modifications:
Finished product

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// show table name by each cargo table value set
$(function() {
	var currTable = '';
	$('.action-pagevalues h2 span, .action-pagevalues #mw-content-text > .wikitable').each(function() {
		if ($(this).hasClass('wikitable')) {
			$(this).attr('style', '--table-name:"' + currTable + '"');
			$(this).addClass('cargo-values');
			return;
		}
		currTable = $(this).text().match(/^"(.+?)".*/)[1];
	});
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
.cargo-values > tr:first-child > td:last-child,
.cargo-values > * > tr:first-child > td:last-child { position:relative; }
.cargo-values > tr:first-child > td:last-child::before,
.cargo-values > * > tr:first-child > td:last-child::before {
 content:var(--table-name);
 text-align: center;
 top:-20px;
 position:absolute;
  width:100%
}

And the second part:

1
2
3
4
5
.action-pagevalues .toc {
 float:right;
 position:sticky;
 top:0;
}

Explanation

In broad strokes, what I do is to create a ::before pseudoelement on each wikitable (really the last cell of the first row of each wikitable) printing the name of the Cargo table. This meets the requirement of “don’t add elements to the DOM that would create a vertical content jump.” How do I get this name? Why…with CSS variables, the absolutely best and most overpowered thing in the world!

You’re able to make the value of a CSS variable into the content of a pseudoelement, so what I do is to transfer the knowledge of “what’s the Cargo table name?” into a display by first setting an inline style of a CSS variable in the JS and then fetching it in the stylesheet.

Let’s do it!

JavaScript

In this snippet, I don’t bother to check I’m on a pagevalues page first because my selector (.action-pagevalues h2 span, .action-pagevalues #mw-content-text > .wikitable) will just return nothing anyway if I’m not. (Arguably you still should perform this check though, so as an exercise to the reader: Add a line of JS to do this!)

First I initialize currTable outside the scope of the loop, which holds the name of the Cargo table that I’m currently looking at.

Now, my selector has two parts, it matches EITHER .action-pagevalues h2 span OR .action-pagevalues #mw-content-text > .wikitable. The first of these is all of the h2 headings on the page, which contain the name of a Cargo table. The second of these is all of the wikitables on the page, which contain the display data. In the first case, I need to update my running value of currTable; in the second case, I need to add a style to the table with the css variable of the current value of currTable (and then do nothing else).

So you can see that I do basically that in the function inside of the selector:

1
2
3
4
5
6
7
8
function() {
    if ($(this).hasClass('wikitable')) {
        $(this).attr('style', '--table-name:"' + currTable + '"');
        $(this).addClass('cargo-values');
        return
    }
    currTable = $(this).text().match(/^"(.+?)".*/)[1];
}

If we’re in a wikitable then I add a style with the css variable and add a class .cargo-values that my CSS will use. Then I return so I do nothing else.

If not, I update currTable. And that’s it for the JS.

CSS

Pretty much every table style you see in MediaWiki writes two versions of its styles, table > * > tr and table > tr, so I just go ahead and follow this precedent unquestioningly. That’s why you see everything duplicated. The reason I guess is compatibility with the <tbody> element, in case that ever changes? I haven’t really read much about it but it makes sense and it’s nbd so whatever.

Now, I want a position:absolute style for my ::before, which means that I have to add a position:relative to the parent (you can read more about that here if you’ve never worked with positioning before). So I’m doing two things here:

  1. Add a relative position to the last cell of the first row of each table
  2. Add an absolute position to a ::before pseudoelement on that same element and put the content inside of it (and then a bit of extra styling stuff)

Then the second part just sets the table of contents to float:right and be stickied to the top with 0 pixels above it when you scroll down.

Conclusion

The “don’t vertically modify the DOM” requirement here definitely made some, uh, creativity necessary. Also, CSS variables are really, really, really cool and make a lot of stuff possible. Finally, the overloading of the word “table” is just unfair. But you should be able to copy-paste this code to your wiki/user page and have it work without modification!

Share on

river
WRITTEN BY
River
River (RheingoldRiver) is a MediaWiki developer and the manager of Leaguepedia. She likes cats.

What's on this Page