I am trying to track how much time certain sections on a page had a certain class.
The page structure is of form:
Topic 1
Subtopic 1.1
Subtopic 1.2
Topic 2
etc
All topics
and subtopics
have anchors
, where the anchor id
is the topic
name. I use the variable scrollItems
to refer to these main topic elements.
I retrieve all anchors
and scrollItems
via
scrollItems = $('div.topicrow-header'),
anchors = $('.navanchor');
So, scrollItems
is a subset of anchors
.
What I am doing is using a sticky
class so that for a given topic
on a page, it has a sticky
header. A topic
might have subtopics
, and if any one of the subtopics
is 'in view', as I determine in view, then the topic's
header is stuck on top of the page as a section heading. So using above, if someone is viewing Topic 1, Subtopic 1.1
, or Subtopic 1.2
, then the topic
whose anchor id
is Topic 1
will have class sticky
.
If someone scrolls quickly, then the detected topics
and subtopics
are changing quickly, and I don't consider that time spent on the material.
I sort of came up with a hard-coded way to track time spent on a topic, but I suspect it has many pitfalls. Here is what I am currently doing.
Note: some of the variables used below are retrieved elsewhere, but are available.
var prevTopicId=0,
curTopicId=0,
prev_idx=null,
scrollItems = $('div.topicrow-header'),
anchors = $('.navanchor'),
topMenu = curles.find('#lessontopics-'+curlesid),
topMenuHeight = topMenu.outerHeight()+15,
cur_idx=null;
$(window).on('scroll', _.throttle(function() {
// Get container scroll position
var fromTop = $(this).scrollTop()+topMenuHeight;
var cur = anchors.map(function(){
if (($(this).offset().top < fromTop) && (this.getBoundingClientRect().top < .25*topMenuHeight )) {
return this;
}
});
// Get the id of the current element
if (cur.length !== 0) {
cur = cur[cur.length-1];
var id = cur.id;
// only set sticky class for topic row headers
if ((cur.classList.contains('topicrow-header'))) {
curTopicId = id;
// first time scrolling, prevTopicId = 0, so sticky class will be added to first topic that comes in view
// then sticky class will only be added to a newly seen topic
if (curTopicId !== prevTopicId) {
console.log('setting active sticky item first time for '+curTopicId);
$.each(scrollItems, function( index, value ) {
if (value.id === curTopicId) {
$(value).addClass("stick");
topic_times[index].startTime = Date.now() / 1000 | 0;
cur_idx = index;
return false;
}
});
}
// first time around, prevTopicId =0, so no sticky class to remove
// then we remove a previous sticky class when we have newly seen topic
if ( prev_idx == null && cur_idx != null) {prev_idx = cur_idx;}
if (prevTopicId !== 0 && curTopicId !== prevTopicId) {
console.log('removing previous active sticky item for ' + prevTopicId);
scrollItems.filter('#' + prevTopicId).removeClass('stick');
nowtime = Date.now() / 1000 | 0;
difftime = nowtime - topic_times[prev_idx].startTime;
if ( difftime > 10 ) {
topic_times[prev_idx].totalTime = topic_times[cur_idx].totalTime + difftime;
topic_times[prev_idx].endTime = nowtime;
} else {
topic_times[prev_idx].startTime = 0;
}
}
prev_idx = cur_idx;
prevTopicId = curTopicId;
}
}
});
Now, the above seems to work, but there is always the question of what to do when a sticky class is added for a topic, and there is no change to another topic. How to count time in that case, when I have no event to trigger me being able to make a time difference calculation?
So seems to me, in the more broad sense, I need an overall method to calculate time an element had a class name.
Anyway, I figure there has to be a tried and true way to do this, one that is sufficiently reliable.
What could I do differently? How would you solve this problem?