Skip to content

Interacting with axis labels #7976

@famschopman

Description

@famschopman

I want to enable interaction with the axis labels. Clicking the axis label would enable a contextual menu with the ability to change the sorting order, aggregation type on measures, or change the way we present dates (e.g. original, by week, by month, by quarter). It is a very nice way of changing the presentation in a nice approachable way.

ChartJS does not support events on ticks and axis labels out of the box (it does on data points) so I have been looking on the best way to accomplish this. The chart object exposes a number of elements that can help. The chart.scales property provides access to labels, ticks, measurements and coordinates.

I want to separate the axis label, from the axis ticks. This enables me to position a regular DOM element on top of the rendered axis label that can also provide hover effects to emphasize to users that they can interact with the label to show a menu.

I have the rough basis ready, it's not rocket science but before I move on I wanted to validate with the community if someone else already build something similar that I can reuse, perhaps ChartJS has already hidden endpoints/extension points?

Either way, for others to use or learn, the rough basis I stitched together. I haven't yet checked what happens if the position was not explicitly defined on the axis etc. so the code is full of assumptions and possible unhandled weaknesses.

    chartWrapper.addEventListener('click', () => {

        let parentElementStyle = window.getComputedStyle(chartWrapper.firstElementChild);
        let paddingTop = parseInt(parentElementStyle.paddingTop);
        let paddingLeft = parseInt(parentElementStyle.paddingLeft);
        let marginTop = parseInt(parentElementStyle.marginTop);
        let marginLeft = parseInt(parentElementStyle.marginLeft);       

        var allScales = visComp.visualization.chart.config.options.scales.yAxes.concat(visComp.visualization.chart.config.options.scales.xAxes);

        for (var i = 0, j = allScales.length; i < j; i++) {

            var scale = allScales[i];            
            var axisInfo = visComp.visualization.chart.scales[scale.id];
            var tickWidth = axisInfo._labelSizes['widest'].width;
            var ticksX;


            var rect = document.createElement('div');
            rect.style.position = 'absolute';            

            switch (scale.position) {
                case 'left':
                    ticksX = axisInfo._labelItems[0].x - tickWidth;
                    rect.style.top = axisInfo.top + paddingTop + marginTop + 'px';
                    rect.style.left = axisInfo.left + paddingLeft + marginLeft + 'px';
                    rect.style.width = axisInfo.right - ticksX + 'px';
                    rect.style.height = axisInfo.height + 'px';
                break;
                case 'right':
                    ticksX = axisInfo._labelItems[0].x + tickWidth; 
                    rect.style.top = axisInfo.top + paddingTop + marginTop + 'px';
                    rect.style.left = axisInfo.right - tickWidth + paddingLeft + marginLeft + 'px';
                    rect.style.width = axisInfo.right - ticksX + 'px';           
                    rect.style.height = axisInfo.height + 'px';
                break;
                case 'bottom':
                    console.log(axisInfo);
                    tickHeight = axisInfo._labelSizes['highest'].height;
                    rect.style.top = parseInt(axisInfo.top + axisInfo.paddingTop + paddingTop + marginTop + tickHeight) + 'px';
                    rect.style.left = (axisInfo.left - axisInfo.paddingLeft) + paddingLeft + marginLeft + 'px';
                    rect.style.width = axisInfo.maxWidth + 'px';
                    rect.style.height = parseInt(axisInfo.height - tickHeight) + 'px';
                break;
                 case 'top':
                    tickYCoord = axisInfo._labelItems[0].y;
                    rect.style.top = axisInfo.top + paddingTop + marginTop + 'px';
                    rect.style.left = (axisInfo.left - axisInfo.paddingLeft) + paddingLeft + marginLeft + 'px';
                    rect.style.width = axisInfo.maxWidth + 'px';
                    rect.style.height = tickYCoord + 'px' ;     
                break;
            }

            
            rect.style.border = '1px solid red';
            rect.style.zIndex = '99999';
            //rect.style.transformOrigin = "top left";
            //rect.style.transform = 'rotate(-270deg)';
            //rect.innerHTML = scale.id;
            
            rect.addEventListener('contextmenu', ()=>{alert('show the menu')});            
            chartWrapper.appendChild(rect);  
        }        
    });

And it will look like this in my pet project.

image

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions