Styling cells that span multiple rows
I'm attempting to build a timetable using HTML tables and apply a class that visually indicates the current event using a bit of js.
Each row represents 30 minutes, and different events last for different amounts of time.
I've worked out how to apply the styles to cells in a given row, and worked out some logic for selecting the right
<tr>
based on the current time, but longer events span multiple rows and only receive the styling while the first 30-minute chunk (i.e. the row that the cell starts in) has the class applied.
Is there any way to apply a class/style to a cell by applying styles to one of the rows it spans? If not, any advice on the cleanest way to apply styles to those cells? Right now, the best I can come up with is putting a data attribute on every cell for the time window its in and comparing it to the current time, which feels a bit ridiculous.
Thanks!
https://codepen.io/Beanie127/pen/dyxXyLO99 Replies
what is your expected result?
from the text, i can't imagine it
So, Breakfast spans 3 rows, 07:00 through 08:00. While the time is between 07:00 and 08:30, I want to be able to apply the class
.current-time
to the cell.
Right now, if the time is between 07:00 and 07:29, the class is applied to all cells in the row 07:00, so the cell containing Breakfast is highlighted, but if the time is 07:30, even though the cell for Breakfast spans that row, it doesn't receive the style, because the <td>
isn't actually in that row.
I want to know if there's a clever way to apply a style to that cell, or to cells that span a given row, even though the cell isn't actually in that row.i see what you mean
I hate working with time
uh ... where's the data?
is there any reason why you don't have this in javascript?
I've attempted to build a version of this in JS; my data management skills are such that I can't work out the best way to notate it.
Each workshop session actually has multiple different options within it, some but not all of which are repeated at different points throughout the event, which spans several days, and there are a bunch of locations to consider as well.
If possible, I just find it so much easier to write it all in HTML rather than try and build a data architecture to represent it all in JS, just to then render HTML again.
The actual data currently exists as a bunch of spreadsheets and text documents
the problem is that now you have to read the data from the dom
which is ... eh
i think you should start over on your code, by the way
I'm open to suggestions; what issues do you find with the code?
for starters,
(hours - 7) * 2
you also remove the class and add it back again tot he same element
also, you have a syntax errorThat should be
(hours - startTime) * 2
, because the day runs in 30 min incrementsthe console said this: 🤔
It removes the class from all elements first, so that when the time updates, it doesn't remain on the previous element
class updates should be done at the end, because you can trigger a re-draw
I don't understand what that means, sorry
it means that, if you remove the class, if you're unlucky, you can cause a redraw on the page
which has performance implications
also, you could be changing the class of the element to the same element
also, you could start checking from the first element that's marked as
current-time
, and have some speedups and simplify your codeI understand that this can be optimised, certainly. This is minimum viable product to demonstrate the effect I'm trying to achieve. My question is more about how to style the relevant cell
so, the code is working fine?
Whatever time I set "hours" and "mins" to, this code reliably selects the row which matches that time and applies the class to it. But that doesn't affect cells which span that row. I want to know either if there is a way to select cells which span a given row, either via CSS or JS, so that I can style it.
so, it isn't working fine?
no
then lets make it work
start by deleting everything except the first line, on a different place
of the JS, right?
yes
but do it somewhere else
I've forked the pen
good
the idea is simple
write a function that, given the
timeTable
as an argument and a current time (minutes and seconds, together or separated, however you wish) outputs the following:
- 000:00 - 06:59 -> null
- 07:00 - 23:59 -> node
it can be the first child
and no, im not writting this for you
the idea is to return any event from the tablewhat about the minutes?
oh, wait, you're passing an object with hours and minutes?
yeah
personally, i wouldn't do it like that, but that's fine
now, give that function a proper name
getNode
is technically correct, but meaningless
a better name is, for example, getCurrentEvent
the name has to describe to the human reading what the function is forUnderstood
now, i need a final decisions from you: is this going to have 1 and only 1 event for a time, forever?
Yes
okay, now, on a
for ... of ...
loop, show all elements in the consoleno, in the same function
everything im going to tell you, for a bit, is in the function
no, not an
in
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
never ever ever use for( ... in ...)
for arrays and array-like objects
if you forget to set something as non-enumerable, it will show in the loop
which is very very weird for arraysI'll have a proper look over that link later, but for now I'll take your word for it
also, wth did i post that as code? 🤦
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of <-- this is an important read, so you understand the syntax
should that be
for (let timeslot in timeslots)
then? Do I need to explicitly assign it as a variable?that's an
in
, not an of
those are 2 different loopsall the examples on MDN include a
const
declarationyou can use any
okay, cool
const, let, var
Receives a value from the sequence on each iteration. May be either a declaration with const, let, or var, or an assignment target [...]
okay, so we've got a reference to each row of the table and we're iterating over them and logging each one to console
exactly
this is just so you're familiar with how the loop works
now, you need to create a constant outside the function
that constant will have the minimum time
the minimum time is 7
after that, replace the
7
in the function with that constantthat function needs space to breathe
also, you're not returning
null
, you're returning undefined
better?
much better
now, you need 2 variables: current time and current element
the current time starts from minimum time and the current element starts at null
actually, we will have to do something different
we will need the
do { ... } while()
loop instead
i forgot to account for the minutes
so, the idea is simple: if there's a 2nd child inside the row, that means there's an event there
actually, if you want
a for
loop works too
the idea is to increment the current time by 0.5, while it is lower than 24
and then, to get the index, you can subtract minTime
and multiply by the number of events per hour (another constant to create outside of the function), which is 2
actually, that 0.5 needs to be a constant, because it is the time "step" - how much it moves forward
so, the value of that constant is 1 / <num events per hour>
and then, the current time increments by that step
So this is what I have so far
the for loop has to be changed for an actual
for(...; ...; ...)
loop
i know i said to use that, but i made a mistake and forgot the minutesI'm slightly confused on what I'm iterating over; like this?
you should move the variable declaration to the loop
i know it is a bit long
now, the idea is to log the index
so, the index has to start at 0, then 1, then 2 ...
and the index is
(currentTime - minTime) * eventsPerHour
is it working?
seems to be!
and it returns the indexes?
should go all the way to 47 or 48 or something
wait, no
way less
33
If I feed it a time of 7am, it returns indexes 0–33
yes, that's correct
now, log each first child of the <tr>
console.log(timeslots[index].firstChild)
gives me a bunch of
`
followed by an error: cannot read properties of undefinedand what did the
timeslots[index]
give you?every row, then 'undefined'
presumably because the table ends at 23:00 and we're still going at 23.5
turned maxTime into a variable for clarity
yup, maxtime is 23.5
and now, get the 2nd child
if there isn't one, continue the loop
if there's one, show in the console
and also show the time that it is
instead of always writting that long thingy, you can store it into a variable
you can call it
element
or event
or something
also renamed
currentTime
because the actual current time is time.getHours()
that we're passing into the functionif you're going to use
t
, might as well use i
because it is the variable usually used for a for
loop
it's just a convention, and you can keep using t
it's perfectly fine
so, now the idea is simple: you have to compare the time in the loop with the start time
you have to calculate the end time of the event
which is pretty easy: it's the value in rowspan
multiplied by timeStep
and the end time is correct?
well, it shows the time that the event ends, yes, but whether that's the number we actually need, I don't know, because that's the time that corresponds to the next timeslot
that's expected
if it starts at 7 and ends in 30 minutes, it will be 7.5
in which case, yes, it's correct
it's accurate 😛
now, you need to convert the time from a date object into a float
and you do it by doing
hours + (60 / minutes)
that value will be needed multiple times, so, cleate it outside the loop
and it won't change, so, a constantnow, check if the current time is between the start and end time
if isn't, continue the loop
if it is, set
currentElement
to that <td>
and break the loop if (!(t < currentTimeFloat < endTime)) continue;
is that syntax right? I'm not sure about negating multiple conditionsthen return
currentElement
no, that's python syntax
if you are okay with multiple ifs, then you can check if the current time is lower than the start time and then check if the end time is higher than the current timeIs there a reason I shouldn't just do this and otherwise leave the loop to continue?
yes: that doesn't really exist, as far as i know
yeah, it always returns
true
you can check if the current time is equal or higher than the loop time
and then check if the current time is lower or equal to the end timeand
not 2 ifs
the result is the same, but is lower brace hell
that's better
now, test the function
doesn't seem to be working
I had the currentTImeFloat calculation wrong, was multplying instead of dividing; I fixed that, and it still doesn't return
🤔
because I'm comparing currentTime instead of currentTimeFloat, fuck me
yup
that did it
i actually didn't notice it
dunno where you are but it's 4am here in the UK, that's certainly a factor for me
but does it return the right element?
it's a good time to sleep
it took a while to re-write it
it doesn't
but it is returning an element
and I can troubleshoot later
thank you very much for your help, this has been incredibly informative
you're welcome