What’re you eating? Part 10
Now it’s time to implement a bit of JavaScript (JS). I’m not going to make a habit of doing my writeups so piecemeal but I wanted to write about this specifically because I’m learning it and it’s a change from how I was doing a few things before. I had a couple of main ideas I wanted to use JS for:
Making my navigation links better
Barcode searching
Table sorting
I’m going to implement barcode searching in a later blog, so I’ll quickly cover off on 1 before spending more time on 3. But first, a quick word about my dev environment.
Basically, I’ve been pretty unhappy with it and I needed a new way to do things. My work flow was generally:
Write code that should work in Visual Studio Code (VSC)
Connect to the pi with PuTTY and create a file with the same name
Copy the contents across into the new file
Adjust, using nano, all the errors that cause problems
This was leading to bad formatting, a slow update process, and multiple versions of my code across different folders/machines and wasn’t sustainable. Instead, I downloaded WinSCP which allows for files to be moved back & forward between devices. I now have 2 choices: I can either edit local copies of files and then drag the changes over to the pi for testing, or WinSCP lets me edit the pi files directly in VSC - I just save it and the changes are moved across automatically. It’s a really nice feel and it’s improved my dev speed considerably - don’t just continue with how you started, if there’s a chance for improvement, take it!
Now, back to our regularly scheduled programming: improving my navigation links. You might remember they looked something like this:
<table rules="cols"> <tr> <td><a href="index.html"><div>Pantry Updater</div></a></td> <td><a href="pantry_list.php"><div>Pantry List</div></a></td> </tr> </table>
Where I designated a <div> tag as a link to the target page. It worked, but that’s about all it did - it was a relief for me to see how easy it was to find a solution with JS.
<table class="navTable" rules="cols"> <tr> <th class="sortHeader" onclick="location.href='index.html'">Pantry Updater</th> <th class="sortHeader" onclick="location.href='pantry_list.php'">Pantry List</th> </tr> </table>
And just like that - it works. If you remember from the last blog, the sortHeader class just makes the cursor change to indicate it’s a link. Now I can get to my table sorter.
This is something I’ve wanted to implement for a while but didn’t really know how to realise it - the pantry list should be able to be sorted any which-way based on what I might be looking to see. I’m a lazy man and see little to no benefit in reinventing the wheel, so a quick search online turned up a plethora of people willing to help out with my issue - turns out a lot of people want to sort their tables! I grabbed the following and tried to implement it:
const getCellValue = (tr, idx) => tr.children[idx].innerText || tr.children[idx].textContent; const comparer = (idx, asc) => (a, b) => ((v1, v2) => v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) ? v1 - v2 : v1.toString().localeCompare(v2) )(getCellValue(asc ? a : b, idx), getCellValue(asc ? b : a, idx)); // do the work... document.querySelectorAll('th').forEach(th => th.addEventListener('click', (() => { const table = th.closest('table'); Array.from(table.querySelectorAll('tr:nth-child(n+2)')) .sort(comparer(Array.from(th.parentNode.children).indexOf(th), this.asc = !this.asc)) .forEach(tr => table.appendChild(tr) ); })));
Straight from Nick Grealy’s explanation:
1. add a click event to all header (th) cells...
2. for the current table, find all rows (except the first)...
3. sort the rows, based on the value of the clicked column...
4. insert the rows back into the table, in the new order.
JavaScript is still on my list to learn, but in the future - right now I have too much else on to dedicate to learning a whole new language. That said - I did go through to try and understand the syntax and each step which I find is a useful endeavour when starting out. As I said before, I tried to implement it but failed - I actually tried several solutions that were provided in that thread, but none worked. I had to do some more research to find out what the problem was, with someone helpfully suggesting to include console.log('test 123') at the top of the script to see if the script is being called at all. On the left is what I originally tried, and then I changed it to the right side which actually printed “test 123” to the console.
<body> /* BODY */ <script> /* SCRIPT */ </script> </body>
<head> <script src="sort.js"></script> </head> + sort.js contained /* SCRIPT */
I’m not 100% sure why the left solution didn’t work, but since I’d just gone on a CSS rampage to clean up my code, it seemed like an ok idea to make the code external. The small hiccup I had was that my table wasn’t sorting - the test line was being printed but nothing else was happening. This led to me trying to test the code on a few online testing websites which all came back to say the code was sound (which didn’t improve my mood at all) - but then I saw someone explain exactly my problem. If I’m executing the code in the header, before any of the HTML elements are loaded, then the script will be silently failing - it can’t interact with a table that isn’t there, so just stops. It turns out - I had the script in the correct place originally, just the wrong format. I make a slight change to allow elements to load before starting the script:
<body> /* BODY */ <script> <script src="sort.js"></script> </script> </body>
And it works! I can now sort based on any column in my pantry_list.php page. Now, what to do next?