'"Zooming" a brushX domain?

I've made a timeline tool based off of Mike Bostock's old Brush & Zoom example. It works great when the date range is fairly simple but becomes unworkable when there are clusters of events (e.g. hourly) within a longer time range (e.g. days or weeks). The brush becomes too thin to be usable and the user is left trying to fiddle with zooming and panning in order to see the data (as in the example below).

enter image description here

As a first attempt at a solution I created a context menu for the brush and use the brush extent to redefine/filter the data based on the brush range (I may be using the wrong terms here). It 'sort of' works though it is a clunky and imprecise "one shot" method. Results and code below.

I am thinking that if I could "zoom" the brush (or "brush the brush") that would be a more interactive and user friendly way of working with this type of data situation. I've searched around for d3 examples and haven't found any. I am also concerned that my "subtimeline" approach won't be performative interactively since it redefines the date set and rebuilds the timeline.

I am interested in any ideas about how to handle this sort of data situation and/or if this "brushing the brush" is a dead end. Is there a better d3 way to handle this?


enter image description here


enter image description here

(edit: the display date for the last event above reads 10:50 – that is wrong, it should be 11:50 which is what is in the data)


// code edited for clarity
function createSubtimeline() {
    subtimelineDates.push(moment(x.domain()[0], "L LT"));
    subtimelineDates.push(moment(x.domain()[1], "L LT"));

    updateData()
}

function updateData() {

    var activeData
    if (subtimelineDates.length != 0) {

        var firstDate = subtimelineDates[0];
        var lastDate = subtimelineDates[1];
        activeData = timelineJson.events.filter(function (e) {
            var startDate = moment(e.startDate, "L LT");

            if (startDate.isAfter(firstDate) && startDate.isBefore(lastDate)) {
                if (e.eventHidden == false) {
                    return true
                } else {
                    return false
                }
            } else {
                return false
            }
        });
    } else {
        activeData = timelineJson.events.filter(event => event.eventHidden == false);
    }

    var tStart = moment(activeData[0].startDate, "MM/D/YYYY h:mm:ss a");
    var tEnd = moment(activeData[activeData.length - 1].startDate, "MM/D/YYYY h:mm:ss a");

    // update timeline range
    x.domain([tStart, tEnd]);
    x2.domain([tStart, tEnd]);
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source