The Web Design Group

... Making the Web accessible to all.

Welcome Guest ( Log In | Register )

 
Reply to this topicStart new topic
> Force a table to rezise to fill screen, Force a table to rezise to fill screen
Cartoony
post Apr 20 2024, 02:19 AM
Post #1





Group: Members
Posts: 5
Joined: 27-September 23
Member No.: 29,062



I have the following code which displays a scoreboard. I didn't write the original code, I just rearranged the table & added a few additiona elements
This will be viewed on a phone
What I want to to is make the table fit on (or even better, fill) the page
I can specify a width of, say, 95% which is fine, but on some phones that means the bottom of the table is off the page & you have to scroll.
I'd like to make it have a height of 95% too but when I change width to height at line 100 ot does make the table narrower but it's still off the page
Can someone please help me make this table adjust to fit the screen

[code]
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<link rel="stylesheet" type="text/css" href="./css/spectatornobordersmassive.css">

<script src="./jquery/jquery-3.5.1.min.js"></script>
<!--<script src="./jquery/jquery.mobile-1.3.0.min.js"></script>
<script src="./jquery/mobiscroll.custom-2.4.4.min.js"></script> -->
<script type="text/javascript">

$(function() {

getStatus();

});

function getStatus() {

$fileName = "score.json?date=" + new Date().toLocaleString(); //ask for the score.json file generated by updateSpectator.php. Include the date to stop the browser caching an old copy
$.getJSON($fileName, function(data) {

//check if the batsman numbers are set, if they are lets show them!
// if (data.batanumber == null)
// {
// document.getElementById("batANumber").className = "batsman_letter";
// document.getElementById("batBNumber").className = "batsman_letter";
// document.getElementById("batANumber").innerHTML="A";
// document.getElementById("batBNumber").innerHTML="B";
// }
// else
{
document.getElementById("batANumber").innerHTML=formatDigits(data.batanumber);
document.getElementById("batBNumber").innerHTML=formatDigits(data.batbnumber);
document.getElementById("batANumber").className = "batsman_digit";
document.getElementById("batBNumber").className = "batsman_digit";
}

document.getElementById("batAScore").innerHTML=formatDigits(data.bata);
document.getElementById("batBScore").innerHTML=formatDigits(data.batb);
document.getElementById("total").innerHTML=formatDigits(data.total);
document.getElementById("overs").innerHTML=formatDigits(data.overs);
document.getElementById("wickets").innerHTML=formatDigits(data.wickets);
document.getElementById("runsreq").innerHTML=formatDigits(data.runsreq);
document.getElementById("lastman").innerHTML=formatDigits(data.lastman);
document.getElementById("lastwkt").innerHTML=formatDigits(data.lastwkt);
document.getElementById("pship").innerHTML=formatDigits(data.pship);
//NEW TR re additional data
//document.getElementById("runsreq").innerHTML=formatDigits(data.runsreq);
//document.getElementById("lastman").innerHTML=formatDigits(data.lastman);
//document.getElementById("lastwkt").innerHTML=formatDigits(data.lastwkt);
//document.getElementById("pship").innerHTML=formatDigits(data.pship);
//END TR re additional data

//check to see if duckworth lewis exists, as this affects the layout.
// if(data.dltarget == null)
// {/
// document.getElementById("target2Heading").className = "noborder";
// document.getElementById("target2Heading").innerHTML="";
// document.getElementById("target2").className = "noborder";
// document.getElementById("target2").innerHTML="";
// document.getElementById("target1").innerHTML=formatDigits(data.target);
// document.getElementById("dl").innerHTML=formatDigits(data.dltarget);
// }
// else
{
document.getElementById("target2Heading").className = "noborder";
document.getElementById("target2Heading").innerHTML="";
document.getElementById("target2").className = "noborder";
document.getElementById("target2").innerHTML="";
document.getElementById("target1").innerHTML=formatDigits(data.target);
document.getElementById("dl").innerHTML=formatDigits(data.dltarget);
}

});
setTimeout("getStatus()",10000); //rerun the function every 10 seconds (including loading the score.json file)
}

function formatDigits(digits) {
var returnStr="";
//split the value into its digit parts, as we want to strip the dashes
digitArray=digits.split("");
for (i=0; i<digitArray.length; i++) {
//ignore this digit if it is a dash
if (digitArray[i] != "-") {
returnStr=returnStr.concat(digitArray[i]);
}
}
// if the input was all dashes, then lets return a 0
if (returnStr==""){
returnStr="0";
}
return returnStr;
}
</script>
</head>

<body>
<center>
<table width="95%" id="topRow">

<tr class="rowpad">
<th colspan="6" align="center" valign="middle"><h2 align="center">Total</h2></th>
</tr>
<tr class="rowpad">
<td colspan="6" align="center" valign="middle" class="digit" id=total ><h2 align="center">-</h2></td>
</tr>
<tr class="rowpad">

</tr>
<tr class="rowpad">
<th colspan="3" align="center" valign="middle"><div align="center"></div></th>
<th colspan="3" align="center" valign="middle" id=dlHeading2><div align="center"></div></th>
</tr>
<tr class="rowpad">
<th colspan="3" align="center" valign="middle"><h2 align="center">Wickets</h2></th>
<th colspan="3" align="center" valign="middle" id=dlHeading><h2 align="center">Overs</h2></th>
</tr>
<tr class="rowpad">
<td colspan="3" align="center" valign="middle" class="digit" id=wickets ><h2 align="center">-</h2></td>
<td colspan="3" align="center" valign="middle" class="digit" id=overs><h2 align="center">-</h2></td>

</tr>
<th colspan="3" align="center" valign="middle"><h2 align="center"> </h2></th>
<th colspan="3" align="center" valign="middle"><h2 align="center"> </h2></th>
<tr class="rowpad">

<th colspan="3" align="center" valign="middle" id=target1Heading><h2 align="center">Target</h2></th>
<th colspan="3" align="center" valign="middle"><h2 align="center">Runs Req</h2></th>
</tr>
<tr class="rowpad">
<td colspan="3" align="center" valign="middle" class="digit" id=target1><h2 align="center">-</h2></td>
<td colspan="3" align="center" valign="middle" class="digit" id=runsreq><h2 align="center">-</h2></td>
</tr>
<th colspan="3" align="center" valign="middle"><h2 align="center"> </h2></th>
<th colspan="3" align="center" valign="middle"><h2 align="center"> </h2></th>
<tr class="rowpad">
<th colspan="2" align="right" valign="middle" ><h3 align="center">Bat No</h3></th>
<td align="left" valign="top" class="batsman_digit" id=batANumber><h2 align="center">-</h2></td>
<th colspan="2" align="right" valign="middle" ><h3 align="center">Bat No</h3></th>
<td align="left" valign="top" class="batsman_digit" id=batBNumber><h2 align="center">-</h2></td>
</tr>
<tr class="rowpad">
<td colspan="3" align="center" valign="middle" class="batscore_digit" id=batAScore><h2 align="center">-</h2></td>
<td colspan="3" align="center" valign="middle" class="batscore_digit" id=batBScore><h2 align="center">-</h2></td>
</tr>
<th width="16%" align="center" valign="middle"><h2 align="center"> </h2></th>
<th width="17%" align="center" valign="middle"><h2 align="center"> </h2></th>
<th width="17%" align="center" valign="middle"><h2 align="center"> </h2></th>
<th width="17%" align="center" valign="middle"><h2 align="center"> </h2></th>
<th width="17%" align="center" valign="middle"><h2 align="center"> </h2></th>
<th width="16%" align="center" valign="middle"><h2 align="center"> </h2></th>
<tr class="rowpad">
<th colspan="2" align="center" valign="middle"><h3 align="center">Last Wkt</h3></th>
<th colspan="2" align="center" valign="middle" ><h3 align="center">Pship</h3></th>

<th colspan="2" align="center" valign="middle" ><h3 align="center">Last Man</h3></th>
</tr>

<tr class="rowpad">
<td colspan="2" align="center" valign="middle" class="batscore_digit" id=lastwkt><h2 align="center">-</h2></td>
<td colspan="2" align="center" valign="middle" class="batscore_digit" id=pship><h2 align="center">-</h2></td>

<td colspan="2" align="center" valign="middle" class="batscore_digit" id=lastman><h2 align="center">-</h2></td>
</tr>

<tr>





<td id=target2Heading> </td>
<td class="digit" id=target2> </td>


</tr>

</table>
</center>
</body>
</html>
[code]
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post Apr 20 2024, 03:25 PM
Post #2


.
********

Group: WDG Moderators
Posts: 9,663
Joined: 10-August 06
Member No.: 7



QUOTE(Cartoony @ Apr 20 2024, 09:19 AM) *

What I want to to is make the table fit on (or even better, fill) the page

This will only work if the page viewport is large enough to contain all the table's content (by their nature, tables want to take a shape and size based on their content):

CODE
body {margin: 0; padding: 0;}
table {width: 100vw; height: 100vh;}

(the "vw" and "vh" units mean viewport percentage width and height, respectively).

It may also help to put

CODE
<!doctype html>

at the top of the HTML file, right before the <html> start tag, and

CODE
<meta name="viewport" content="width=device-width">

in the HEAD section of the page.

There may or may not be other issues with the code, I just took a quick glance. Much of the table content seems to be pulled from a database(?) and is thus not visible in the code example, and I don't know what the external JS and CSS files contain.

This post has been edited by Christian J: Apr 21 2024, 06:45 AM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Jason Knight
post Apr 21 2024, 06:30 AM
Post #3


Advanced Member
****

Group: Members
Posts: 107
Joined: 25-December 22
Member No.: 28,719



I'd have to see the data plugged into that table to truly weigh in, but the simple fact is a lot of tabular data is not practical to show on smaller displays. Period. Do not pass go, do not collect $500 in human-malware relief.

That said, there are so many warning signs all across that code of outdate, outmoded, and even insecure practices. From innerHTML or remote content, to jQuery (sucker-bait that should never have even existed), brute force getting elements every blasted time, static scripting in the <head> where it could fail and isn't cached...

But the markup? ouch. Tags and attributes that haven't even EXISTED in HTML for 27 year? Numbered headings inside table cells? Missing opening <tr>?

I'd toss that entire mess and start over with code for this century. I'd be willing to help with that if I could see the data and page in question.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Cartoony
post Apr 21 2024, 08:50 AM
Post #4





Group: Members
Posts: 5
Joined: 27-September 23
Member No.: 29,062



QUOTE(Jason Knight @ Apr 21 2024, 12:30 PM) *

I'd have to see the data plugged into that table to truly weigh in, but the simple fact is a lot of tabular data is not practical to show on smaller displays. Period. Do not pass go, do not collect $500 in human-malware relief.

That said, there are so many warning signs all across that code of outdate, outmoded, and even insecure practices. From innerHTML or remote content, to jQuery (sucker-bait that should never have even existed), brute force getting elements every blasted time, static scripting in the <head> where it could fail and isn't cached...

But the markup? ouch. Tags and attributes that haven't even EXISTED in HTML for 27 year? Numbered headings inside table cells? Missing opening <tr>?

I'd toss that entire mess and start over with code for this century. I'd be willing to help with that if I could see the data and page in question.


This is a project that was started by others many years ago and coded by amateurs (you can tell, right!). It's published here: https://buildyourownscoreboard.wordpress.com/ it's for a cricket scoreboard and is completely not for profit.
It's not connected to the Internet.
A Pi runs a Web server and the scorer connects to it over WiFi and has a html interface for doing the scoring.
The Pi sends the data to an Arduino which controls shift register chips that turn LEDs on & off to display the digits on the scoreboard.
I have hacked around in the code and added more variables but fundamentally it's still the original amateur code.
TBH starting again would definitely be good but it would be a huge task and I'm sure I have no idea where to start.
A zip of the whole lot is in this zip https://we.tl/t-DSQDDxgy1h but as you'll see it's a right mess. That said it does work.
You're advice would be appreciated but I'm well out of my depth rewriting it.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Cartoony
post Apr 21 2024, 08:53 AM
Post #5





Group: Members
Posts: 5
Joined: 27-September 23
Member No.: 29,062



QUOTE(Christian J @ Apr 20 2024, 09:25 PM) *

QUOTE(Cartoony @ Apr 20 2024, 09:19 AM) *

What I want to to is make the table fit on (or even better, fill) the page

This will only work if the page viewport is large enough to contain all the table's content (by their nature, tables want to take a shape and size based on their content):

CODE
body {margin: 0; padding: 0;}
table {width: 100vw; height: 100vh;}

(the "vw" and "vh" units mean viewport percentage width and height, respectively).

It may also help to put

CODE
<!doctype html>

at the top of the HTML file, right before the <html> start tag, and

CODE
<meta name="viewport" content="width=device-width">

in the HEAD section of the page.

There may or may not be other issues with the code, I just took a quick glance. Much of the table content seems to be pulled from a database(?) and is thus not visible in the code example, and I don't know what the external JS and CSS files contain.


Thanks for taking the time to look & reply.
Please see my response to the next post, all the code is in the zip file which is probably easier than me trying to explain something I'm not that familiar with!
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Jason Knight
post Yesterday, 07:49 AM
Post #6


Advanced Member
****

Group: Members
Posts: 107
Joined: 25-December 22
Member No.: 28,719



Sadly your .zip download expired before I even noticed you replied. :/

User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Jason Knight
post Today, 08:48 AM
Post #7


Advanced Member
****

Group: Members
Posts: 107
Joined: 25-December 22
Member No.: 28,719



I went ahead and did a rewrite based on the site you linked to which gave me a better idea what's going on. I took some stylistic liberties as I was guessing without your .zip, adding my own colouration, gradients, and style... I even tossed my own "interface" font at it which has a nice LCD/LED/VFD style number set in it.

Here's what I came up with:

https://cutcodedown.com/for_others/cartoony/scoreboard.html

as with all my examples the directory:
https://cutcodedown.com/for_others/cartoony/

Is wide open for easy access to the gooey bits and pieces, and I tossed a .rar in there of the whole shebang for good measure:

https://cutcodedown.com/for_others/cartoony/cartoony.rar

I tested on everything from three different handhelds -- ancient 480p tablet, iPhone SE (emulated in OSX), and a Ulephone Power Armor 13 -- right up to the 65" 4k display on my media center, and it seems to scale well.

The magic actually happens in this line of CSS:

CODE
font-size:clamp(9px, min(2.95vw, 3vh), 1.5rem);


Which scales the entire layout to fit-to-screen. By declaring that font-size on body, and then declaring EVERYTHING in the layout in EM, it will expand/contract to the display size. The inner "min" sets what percentage of width and height to use, the first number in clamp being the smallest text to allow (anything under 9px is usually garbage) and the last number being the upper size limit so that it doesn't become TOO much of a fist in the face.

Though if you want it to be a fist in the face you can always up that value.

There's a LOT going on in that rewrite and it's close to my bedtime, but in the AM (well, my AM which should be around 7PM local time... non-24 sleep-wake disorder is fun) I'll do a complete breakdown for you of the how/what/why of the rewrite that hopefully you and others can learn from.

I have a very different (old school) approach to problem solving, and it's very much reflected in this rewrite.

This post has been edited by Jason Knight: Today, 08:51 AM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Jason Knight
post Today, 02:15 PM
Post #8


Advanced Member
****

Group: Members
Posts: 107
Joined: 25-December 22
Member No.: 28,719



Alright, insomnia so let be break down how this works and the choices I made in implementing it.

The first thing I do on any project is go "what IS the data?". Not just what the data contains, the information, but the structural and grammatical meaning of it all.

So we're basically starting out with this in JSON

CODE
{
    "total"      : "0",
    "wickets"    : "0",
    "overs"      : "0",
    "target1"    : "0",
    "runsreq"    : "0",
    "bata"       : "0",
    "batb"       : "0",
    "batanumber" : "0",
    "batbnumber" : "0",
    "lastwkt"    : "0",
    "pship"      : "0",
    "lastman"    : "0"
}


We have titles and values. Ok, there are a number of ways this information would/should be conveyed. A table row for example:

CODE

<tr class="total">
  <th scope="row">Total</th>
  <td>0</td>
</tr>


The table heading with row scope defining what the TD (table data cell) MEANS. This is a far cry from the markup you had which cared not one iota for what any of the tasgs used actually MEANS. This: (what the original had)

CODE
<tr class="rowpad">
<th colspan="6" align="center" valign="middle"><h2 align="center">Total</h2></th>
</tr>
<tr class="rowpad">
<td colspan="6" align="center" valign="middle" class="digit" id=total ><h2 align="center">-</h2></td>
</tr>


Is a mix of "tables for layout" and "presentational layout". it provides zero semantic relationship between the TH and TD due to a lack of scope, the data is not a heading so the original was using it just to say "make this large" -- which is not what H2 is supposed to be used for... and all those attributes and classes aren't helping matters.

I could make my table rewrite work, but I find myself again asking what IS the data. The data field is output from JavaScript, and a label describing it! Well, we have tags for that. Label and output... Apart from being values there's no real semantic relationships between the data columns, so a table may all-around just plain be the wrong tag. This is NOT tabular data.

So, instead:

CODE

<label class="total">
  Total<br>
  <output>0</output><br>
</label>


Would be what I'd consider the correct markup for each of these.

Next if we want to keep the grunt-work low and make our client-side scripting as re-usable as possible, constantly doing "getElementById" and brute forcing the data each and every blasted time is not efficient. So first in the script let's make a list of labels as shown on the scoreboard using their associated JSON names as keys. Also rather than having THREE names for things -- displayed label, JSON name, and value ID -- let's use the same names in the script that are used in the JSON. (I know, crazy, right?) both for the JS name and our CSS class!

CODE

        valuesToTrack = [
            // title to show on screen, ...json value names
            [ "Total",     "total" ],
            [ "Wickets",   "wickets" ],
            [ "Overs",     "overs" ],
            [ "Target",    "target1" ],
            [ "Runs Req.", "runsreq" ],
            [ "Bat No.",   "batanumber", "bata" ],
            [ "Bat No.",   "batbnumber", "batb" ],
            [ "Last Wkt.", "lastwkt" ],
            [ "Pship",     "pship" ],
            [ "Last Man",  "lastman" ]
        ], // valuesToTrack


The lable that has two values we just extend the size of the array. Our JavaScript can then use this array of arrays to build the table from the scripting.

Why make the all the label/output from the scripting instead of the HTML? Because this functionality is scripting only. A good basic rule is that if the HTML only works "scripting on/enabled" it should be built on the DOM from the scripting, NOT static in your flat HTML markup.

Thus the entire HTML for a project like this should just be:
CODE

<!DOCTYPE html><html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">

    <link rel="stylesheet" href="scoreboard.css" media="screen">

    <title>Score Card Demo</title>

</head><body>

    <h1>Score Card Demo</h1>

    <noscript>
        <p>
            This page requires JavaScript to function. Please revisit in a scripting capable browser.
        </p>
    </noscript>

    <script src="extensions.js"></script>
    <script src="scoreboard.js"></script>

</body></html>


This way we have "hey dummy you need JavaScript" warning and aren't confusing users by showing a non-functional page.

There are some important things to discuss in there.

The DOCTYPE declaration says this is HTML 5, so do not apply any "quirks mode" rules for legacy IE compatibility. This is important as without it HTML, CSS, and JavaScript in most browsers have some different behaviors.

The charset META replaces the old http-equiv junk. The rest of what that stuff used to set is all "well no s*** Sherlock". Charset should be declared before any normal text or content="" attributes, so best practice is to just make sure it's always the first tag after you open <head>

The viewport META tells mobile devices that we know what we're doing and are designing with small screen compatibility in mind. If you lack this each and every mobile device will assume your page is not written mobile friendly, and thus apply their own styling rules on top of whatever we try to do. Said rules are for ancient legacy sites, do more harm than good, don't generally work as advertised, and worst of all are inconsistent between devices. Thus we tell it "width=device-width" which makes it not lie to us about the screen resolution, and "initial-scale=1" so it basically starts out at "100% zoom" instead of making up some arbitrary zoom level that neither us as designers or the user has any control over."

There are other values people set there, don't do it. Almost all of them disable or screw with the user's ability to zoom in/out.

NOw to aid in building the rest of the Elements on the page I load my extensions.js, a miniaturized gutted subset of my upcoming DOM-JON library.
https://cutcodedown.com/for_others/cartoony/extensions.js

It has three methods that are applied to extend system objects. Extending the system is frowned on in some circles, but honestly the reasons for that (90% of which were for Internet Explorer) are irrelevant, and the rest are gormless pedantry. I prefix my extensions with a double-underscore so as to reduce the odds of other people pulling the same stunt colliding with my code, and to make it clear they are not native methods. (further gutting the arguments against said practice.)

Document.__make - makes a new Element from a tagName and "attaches" attributes and content to it. The first argument is the tagName, latter arguments are things to Element.__attach

Element.prototype.__make - functions as per document.__make, however the newly created element will be appended to the end of whatever your "this" Element is.

Element.prototype.__attach - attaches any arguments passed to it to "this" Element. If it sees an array, it will call this.__make using that array as arguments of the method, where "this" is the current element. If it sees a non-node object it is assigned to "this" element as new attribute values. Otherwise the argument is applied via "append" which will append it flat if it's an instanceof "Node", or stringify any other result.

So if we go
CODE

// document.body is instance of Element, so Element.prototype.__make is called
const newP = document.body.__make("p", { id : "test" }, "This is a test paragraph");


We get basically the same result as:

CODE

document.body.innerHTML += `<p id="test">This is a test paragraph</p>`;
let newP = document.getElementById("test");


Now you might be wondering why go to all that hassle. There are a few good reasons:

1) getElementById can be costly in terms of both performance and the amount of code you write overall. If we could just store the element when we make it, we don't have to jump through hoops to find it later.

2) getelementById and innerHTML can unintentionally re-draw the page before all our changes are done. Another costly -- and often unpredictable -- unwanted behavior.

3) If we're not blanket applying hundreds of elements at once, the DOM method can be faster.

4) innerHTML can be insecure if dealing with user generated data, opening the door to XSS exploits. Not a concern here, but I consider it good practice to just not use innerHTML when other techniques are available.


The advantage of all this should become apparent when we get into the "template" part of this.

Back to our main JS I add an Object I will use to track the application "State".

CODE
scoreCardState = {}


The concept of a "State" is pretty basic. It's where you store all the values, and the "view" is automatically updated via what are known as "getters" and "setters" to show value changes.

This instead of:
document.getElementById("total").innerHTML=formatDigits(data.total);

For each and every blasted one of them, we'll be able to:
scoreboardState.total = data.total;

Opening the door to:
CODE

for (const name in scoreCardState) {
  scoreCardState[name] = event.target.response[name];
}


Replacing ALL of this:
CODE

document.getElementById("batANumber").innerHTML=formatDigits(data.batanumber);
document.getElementById("batBNumber").innerHTML=formatDigits(data.batbnumber);
document.getElementById("batAScore").innerHTML=formatDigits(data.bata);
document.getElementById("batBScore").innerHTML=formatDigits(data.batb);
document.getElementById("total").innerHTML=formatDigits(data.total);
document.getElementById("overs").innerHTML=formatDigits(data.overs);
document.getElementById("wickets").innerHTML=formatDigits(data.wickets);
document.getElementById("runsreq").innerHTML=formatDigits(data.runsreq);
document.getElementById("lastman").innerHTML=formatDigits(data.lastman);
document.getElementById("lastwkt").innerHTML=formatDigits(data.lastwkt);
document.getElementById("pship").innerHTML=formatDigits(data.pship);


You want a different "layout" that all those IF statements were manipulating, you change the values in valuesToTrack. Boom, it just does it.

Then I make the scripted template. I like to put his in a object so that it's all self-contained. That starts out making the <main class="scoreBoard"> element marking the ... well... main content of the page. This is a fixed Element property created at runtime.

CODE

        // template
        template = {

            scoreBoard : document.body.__make("main", { className : "scoreCard" }),


Then come two methods. The first:
CODE

            scoreNode : (titleText, ...valueNames) => {
                const node = template.scoreBoard.__make(
                    "label",
                    { className : valueNames[0] },
                    titleText,
                    [ "br" ],
                );
                for (const name of valueNames) template.scoreOutput(node, name);
            }, // template.scoreNode


Creates our <label> with a class that the same as our first (or only) output object's JSON name. The label node is created directly on the scoreboard's Element that we created on the line prior. It that iterates through all names (we do have a few with more than once) to create the output using this method:

CODE

            scoreOutput : (parentNode, name) => {
                const output = parentNode.__make("output", "0");
                Object.defineProperty(scoreCardState, name, {
                    enumerable : true,
                    get : () => output.textContent,
                    set : (value) => output.textContent = (
                        String(value).replace("-", "") || 0
                    )
                } );
                parentNode.__make("br");
                return output;
            } // template.scoreOutput


The easy part here is making the <output> tag and the break tag after it. What's tricky is that Object.defineProperties on the scoreCardState.

Basically it's adding an enumerable (aka we can see it for looping) get and set methods that use our local "output" variable to return or set the value on the DOM. If "name" is "total" and you call "let value = scoreCardState.total" you will get the result of the "get" method. If you "scoreCardState.total = 10;" the "set" method is run, which filters out hyphens, returning zero if the string is empty. That result is then plugged into our "output" object's textContent.

Because this is its own method, each time scoreOutput is run it will create its own private "output" object separate from every other time it is run. We can then access that in our getter/setters here unique to that "name". This is similar to a technique people call "factories" just instead of returning a new unique context, we're returning the "output" Element itself whilst setting properties elsewhere. And in the process pissing off the "that function does too much" and "wah wah teh evul side effect" nonners.

Those template methods are then called by a simple loop building our labels and outputs thus:

CODE
// populate all the output sections
for (const fields of valuesToTrack) template.scoreNode(...fields);



Finally we have the request for the JSON file. I've set it up to use XMLHttpRequest directly instead of using jQuery's pointelss wrappers. Some people would say "don't waste the code" but really when I'm at a fraction the code you'd have using jQuery, we kind of have to question if they're packing our fudge on that.

The first thing I do here is create an object containing the "attributes" I want to assign to the request. That's saying that the result we want is JSON, and what to do when the load is finished.

CODE

        scoreCardXHRAttributes = {
            onload : (event) => {
                if (event.target.status !== 200) throw new Error(
                    `Error, server reported status code ${event.target.status}`
                );
                for (const name in scoreCardState) {
                    scoreCardState[name] = event.target.response[name];
                }
                // uncomment next line to enable polling
                // setTimeout(scoreCardXHRRequest, pollingTime);
            },
            responseType : "json"
        }, // scoreCardXHRAttributes


"onload" we check if the event was successful. If not we "throw" an error aborting the script and reporting that error in the console. Otherwise we loop through our result (event.target.response) which is already parsed from string to a full JSON object (no need to manually call parse, one of the entire reasons people get suckered into using jQuery and its kine). After we assign all the new values to the state, we can set a timeout to poll for changes.

Side note, a lot of people don't realize that Event.target inside a XMLHttpRequest callback is the XMLHttpRequest object itself. The batshit crazy jumps and scope trickery I've seen people pull just to pass that object with the request is mind-blowing.

I have the timeout commented out for now since I don't need people visiting my site hammering me with file requests. If this was local on a Pi (which seems to be the intent) I'd uncomment the setTimeout line, and set the pollintTime constant up at the top of the code to whatever felt fast enough wtihout overloading the device.

If this were to be actual web facing code, I would delve into the various push technologies like "web sockets", but that is beyond the scope of what I'm doing for you here.

Next we need to actually have a routine to set up the next request:

CODE

        scoreCardXHRRequest = () => {
            const x = Object.assign(new XMLHttpRequest(), scoreCardXHRAttributes);
            x.open("post", "score.json");
            x.setRequestHeader("Cache-Control", "no-cache, no-store, max-age=0");
            x.send();
            // but tell me again how much "easier" or "better" fetch is.
        }; // scoreCardXHRRequest


I create a new XMLHttpRequest to grab the file with, use Object.assign to put the predeclared attributes on it, open the request, set up a header so that we're not caching (removing the need for the ?date= stuff), and then send it.

When the file is ready, scoreCardXHRAttributes.onload is called. Which in turn if you uncomment the timeout will call scoreCardXHRRequest again creating your polling loop.

And to start that whole thing up, I just set it to do the first request when the rest of the page is done loading.

CODE

    // perform first result polling
    addEventListener("load", scoreCardXHRRequest);


So that attempting to run it doesn't interfere with / slow down other file requests like the stylesheet, webfont, etc.

And that in a nutshell is the scripting. I'm probably butting heads with the post size limits here so next post I'll explain the CSS which in many ways is what your real question was about.

I know it seems complicated, but take your time, digest what I'm saying... and if you have questions ask. I will attempt to explain to the best of my ability.

Just know that what I've presented so far is less than half the code you were using, and it's easier to add/remove values from what's presented by just changing the contents of the valuesToTrack Array.

I'll break down the CSS when I get a chance.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Jason Knight
post Today, 03:13 PM
Post #9


Advanced Member
****

Group: Members
Posts: 107
Joined: 25-December 22
Member No.: 28,719



Alright, let's talk CSS. Follow along with the bouncing ball at:
https://cutcodedown.com/for_others/cartoony/scoreboard.css

External stylesheets exist for a reason, use them. As hard fast rule 100% of the time you see a <style> tag and over 90% of the time you see style="" you're looking at code written by people who never learned enough HTML to be using CSS yet. Just as how if you see people choosing their HTML tags, classes, or ID's based on what they want things to look like, they have utterly and completely failed to divine HTML's purpose.

HTML exists to say what things ARE. Grammatically, structurally, organizationally. Its tags have meanings to that end... this is a heading, this sub-heading starts a subsection of the heading preceding it. This is a grammatical paragraph. This is a table of data where the rows and columns have related meanings / organizations.

That's all done so that the user-agent (UA) can best convey that meaning to ALL users, not just the magically perfectly sighted sitting at a high resolution screen. Braille, speech, search, tty -- all valid targets for HTML that don't give a flying purple fish about what things look like.

Which is why CSS is separate from HTML and should be applied separately. You may have noticed in the markup I put:

CODE

<link rel="stylesheet" href="scoreboard.css" media="screen">


That media="screen" is important. It says "this style is for just screen" and thus other user-agents (A browser is a UA but a UA isn't always a browser) won't waste time loading it. Having all the appearance in an external style also means that it can be cached when / if your markup changes.

Another advantage of which being that no other files -- CSS, JavaScript, Images, fonts, etc, etm -- will start loading until after the HTML has finished loading. Thus the more we can move out of the HTML the faster the page. The same can be said of the scripting as by not having to even think about what things look in our JS, we end up with smaller easier to maintain scripting. This applies to server-side languages as well making less to wade through as a developer, and less crap for the server to sit there gluing together into our final markup.

Basically, anyone telling you to put appearance in the HTML is an incompetent jackass talking out their backside, in desperate need of a quadruple helping of sierra tango foxtrot uniform.

And yes I realize that means 99.99% of all "framework" users and creators. There's a reason I call Adam Wathan -- creator of Tailwind -- a fraud and predator! No matter how many witless hapless gormless clueless fanboys refuse to admit what total freaking suckers they are.

Anyhow...

I start out loading the webfont. Some older UA's will ignore it if they're not first, though honestly that's not really a valid concern in any browser made in this decade. I still put it first out of habit.

Next I have a reset. Resets exist because what things look like by default is none of HTML's business, and thus early on browser makers didn't agree on what the defaults were. Likewise some properties -- flex-grow for example -- have inconsistent starting values thanks to changes to the specification between draft and recommendation... and other newer behaviors -- like box-sizing:border-box -- make life easier if we just throw them at everything.

There are larger resets that honestly give resets a bad name. Wasting time setting and changing values that don't need to be messed with. These bloated resets -- like Eric Meyer's "reset reloaded" -- actually gave resets such a bad name many developers refuse to use them altogether instead dealign with the problems on a case-by-case basis as they arise.

Likewise we have smaller miniscule resets like the so-called "universal reset" of

CODE

* { border:0; margin:0; padding:0; }


But that can wreak havoc trying to style certain form elements consistently across different browser engines. Firefox in particular can be a real pisser about htat.

The reset I use is a nice safe middle ground. A compromise between what is inconsistent across browsers, values I find handy to set up ahead of time, but going NO further.

Something a lot of people don't realize is that the HTML tag is actually a rendered element. I leverage this to center <body> using flex-box, so we don't need to slop and extra DIV into the HTML.

CODE

html {
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    height:100%;
    background:linear-gradient(135deg, #06C, #048);
    color:#FFF;
}


Flex it, center it, colour it. The 100% height actually triggers overblow behavior would/should our content get bigger than the window.

CODE

body {
    width:100%;
    max-width:32em;
    margin:auto;
    font-size:clamp(9px, min(2.95vw, 3vh), 1.5rem);
    line-height:1.5;
    font-family:sans-serif;
    border:0.0625em solid #0008;
    box-shadow:0.25em 0.25rem 1em #0008;
}

By default a flex-child will shrink to fit its content. Here I want it to fill out to 32em, but still shrink to smaller than that if we have to. Thus 100% width with a max-width restriction. The auto-margin makes sure that there's scrolling and proper centering should the window just be too small for the table, whilst -- as I mentioned a post or two ago -- using a clamped font-size and "EM" for everything else on the page (instead of the garbage "px" measurement that has no business being used on websites) allows it all to scale. From there it's just some border and shadow to make it purty.

The main heading descripting the ENTIRE page (what H1 MEANS):
CODE

h1 {
    padding:0.25em;
    font-size:2em;
    text-align:center;
    background:linear-gradient(175deg, #89A 15%, #FFF 45% 60%, #8A9    85%);
    border-bottom:0.0625em solid #000;
    color:#000;
}


Doesn't get too complex. Big font, center it, pad it, put a metallic-like gradient behind it, and a border.

The MAIN tag as .scoreCard is where things get interesting.
CODE

.scoreCard {
    display:grid;
    width:100%;
    max-width:32em;
    padding:1em 1em 2em;
    gap:1.5em 0.5em;
    grid-template-areas:
        "wickets    total   overs"
        "targets    total   runsreq"
        "batanumber empty   batbnumber"
        "lastwkt    pship   lastman";
    text-align:center;
    background:linear-gradient(135deg, #333 20%, #111);
}


Display:grid allows us to create table-like layouts irregardless of what the actual HTML is. This only further enhances what we can do in terms of layout and design without compromising the entire reason HTML exists.

In FF it tries to shrink on some devices (firefox on mobile is utter trash, probably why they're fading into obscurity) so I set the same width values as the BODY tag yet again. Padding is pretty straightforward, and "gap" sets the distance between elemetns in a flex or grid container. In this case I put tall vertical gaps and narrower on the horizontal.

Remember (or if you're just learning) CSS properties like gap, padding, margin, and border-width all take values in a clockwise order starting at the top. If a value is missing from the end, the one on the opposite side of the "clock" is used.

grid-template-areas allows us to arrange our "grid" however we like, we just need to assign grid-area names to each of our subsections. We literally just say the names we assign to create our pattern which... well, is about as slick as slick gets. Even more fun the elements do NOT even have to be in the same order in the HTML as we lay them out!

I also felt like extending total down to the batNumber properties was a bit of a wonk, so I used generated content to make a fake "empty" element.

CODE

.scoreCard:before {
    content:"";
    grid-area:empty;
}

.total      { grid-area:total; }
.wickets    { grid-area:wickets; }
.overs      { grid-area:overs; }
.target1    { grid-area:targets; }
.runsreq    { grid-area:runsreq; }
.batanumber { grid-area:batanumber; }
.batbnumber { grid-area:batbnumber; }
.lastwkt    { grid-area:lastwkt; }
.pship      { grid-area:pship; }
.lastman    { grid-area:lastman; }


The labels I make flex containers and remove their BR. Those breaks were there for non-screen users so it's not a run-on sentence. We are using other style here to accomplish the same purpose. Using grid just makes it easier to align and size their content.

CODE
.scoreCard label {
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    gap:0.5em;
    font-weight:bold;
    text-transform:uppercase;
    text-shadow:0.0625em 0.125em 0.125em #000;
}

.scoreCard br {
    display:none; /* the BR are there for non-screen users */
}


The left and right top areas I push down sightly so their label text is better aligned with "TOTAL"

CODE
.wickets,
.overs {
    padding-top:0.25em;
}


Total of course getting a font-size bump and alignment to the top of its container.

CODE

.scoreCard .total {
    font-size:1.5em;
    justify-content:start;
}


Finally we have our OUTPUT tags, which again represent JavaScript output of calculated values.

CODE

.scoreCard output {
    padding:0 0.125em;
    font:normal 2.5em/1em interface,monospace;
    text-align:right;
    background:linear-gradient(135deg, #321, #000);
    border:0.0625em solid;
    border-color:#000 #FFF2 #FFF3 #000;
    color:#FC0;
    text-shadow:
        -0.0625em 0 0.25em #FC0A,
        0.0625em 0 0.25em #FC0A;
}


by upping the font-size we increase all of our relational measurements. That's why EM is superior to garbage like pixels. From there it's jsut some fancy colouration.

A cute "trick" I use is to use generated content to fill it up with the number 8 so that it looks like an unlit segmented display:

CODE

.scoreCard output:before {
    content:"8888";
    display:block;
    margin-bottom:-1em;
    color:#420;
    text-shadow:none;
}


Because we have a 1EM line-height set on output and a monospace font, setting it to block and using a negative margin slides the actual content text up over this background finishing the illusion of how a real-world display looks.

Shadows, borders, and gradients all helping it look like a real display. I may have gone a little overboard on that, but going overboard with using CSS to make semi-realistic looking devices is one of my hobbies, and I was having fun.

See, FUN!
https://cutcodedown.com/for_others/medium_a...d/calc.app.html

It's scary how far you can take it now without resorting to images. Only image on that calculator is the tablecloth texture. There are entire clubs of designers now priding themselves on creating near photorealistic things using nothing more than HTML and CSS.

Anyhow continuing our journey the two sections that have two outputs each:
CODE

.scoreCard .batanumber,
.scoreCard .batbnumber {
    flex-direction:row;
    flex-wrap:wrap;
}

.scoreCard .batanumber output:first-of-type,
.scoreCard .batbnumber output:first-of-type {
    font-size:1.5em;
}


I switched the flex-direction and set up flex-wrapping, shrinking the first output's size by setting a smaller font-size. The larger second output then automagically wraps down to its own line, completing the layout.

Again, I know this looks complicated. But take your time, digest, and play around with it. You have questions, you know where to find me.

Hope this all helps.


User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post

Reply to this topicStart new topic
4 User(s) are reading this topic (4 Guests and 0 Anonymous Users)
0 Members:

 



- Lo-Fi Version Time is now: 3rd May 2024 - 09:43 PM