# How to make an x axis sticky on a D3 chart?

**URL:** <https://talk.observablehq.com/t/how-to-make-an-x-axis-sticky-on-a-d3-chart/6177>\
**Category:** Help\
**Created:** [February 7, 2022, 5:53pm UTC](https://talk.observablehq.com/t/how-to-make-an-x-axis-sticky-on-a-d3-chart/6177 "2022-02-07T17:53:37Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![jcweaver](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/jcweaver/32/6091_2.png) [@jcweaver](https://talk.observablehq.com/u/jcweaver)\
**Post date:** [February 7, 2022, 5:53pm UTC](https://talk.observablehq.com/t/how-to-make-an-x-axis-sticky-on-a-d3-chart/6177/1 "2022-02-07T17:53:37Z")

</div>

I have a chart similar to this one by Mike Bostock: [Lollipop Chart / D3 / Observable](https://observablehq.com/@d3/lollipop-chart)

How can I make the x axis sticky? Essentially, when the user is still scrolling down on the chart, I’d like the x axis to remain on the screen view.

---

<div class="post-metadata">

**Author:** ![shancarter](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/shancarter/32/3471_2.png) [@shancarter](https://talk.observablehq.com/u/shancarter)\
**Post date:** [February 7, 2022, 9:09pm UTC](https://talk.observablehq.com/t/how-to-make-an-x-axis-sticky-on-a-d3-chart/6177/2 "2022-02-07T21:09:24Z")

</div>

You would need to put the x-axis in a different html element, and then use the technique in this notebook: [Sticky Positioning / Fabian Iwand | Observable](https://observablehq.com/@mootari/sticky-positioning)

There is some discussion in this post as well:

> [@Sticky sidebar in cell](https://talk.observablehq.com/t/sticky-sidebar-in-cell/2268):
>
> I was trying to have a sticky sidebar inside a cell, that is a sidebar that sticks to the top of either the viewport or cell and stops at the bottom of the cell - like [here](https://abouolia.github.io/sticky-sidebar/examples/basic.html), just for a cell. I tried all sort of CSS things (e.g. position sticky) and sticky sidebar npm and jquery modules, but failed. [Here’s](https://observablehq.com/@kappelmann/sticky-sidebar) a small template. Any help appreciated.

Hope that helps!

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [February 8, 2022, 2:09pm UTC](https://talk.observablehq.com/t/how-to-make-an-x-axis-sticky-on-a-d3-chart/6177/3 "2022-02-08T14:09:19Z")

</div>

Unfortunately sticky positioning won’t work that well for elements at the top or bottom, because they are likely to be covered by one of Observable’s sticky toolbars.

I would suggest to put the main chart into a scrollable container. You can add this function to your notebook:

```auto
function split(chart, axisHeight = 30, chartHeight = '500px') {
  const axis = chart.querySelector(':scope > g:nth-child(2)');
  const copy = chart.cloneNode();
  copy.viewBox.baseVal.height = axisHeight;
  copy.appendChild(axis);
  return html`<div>
    <div style="overflow-y:scroll">${copy}</div>
    <div style="overflow-y:scroll;resize:vertical;height:${chartHeight}">${chart}
  `;
}

```

Then change the `return` statement your `chart` cell to:

```auto
  return split(svg.node());

```

This gives you a scrollable and resizable container:

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/1/175e183a4c40226a88f920e2529925008eb60423.png)

Note that the axis container div also has `overflow-y` set to `scroll` so that the width of both containers stays identical.

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [February 8, 2022, 2:22pm UTC](https://talk.observablehq.com/t/how-to-make-an-x-axis-sticky-on-a-d3-chart/6177/4 "2022-02-08T14:22:04Z")

</div>

… If you want to try your hand at a sticky axis, as suggested by Shan, then these are the steps:

First, import a helper function that tracks the visible area (i.e., the “viewport”):

```auto
import {observeViewport} from '@mootari/sticky-positioning'

```

Then add a cell with the following code:

```auto
{
  const topOffset = 60;
  const axisSelector = ':scope > g:nth-child(2)';
  
  const clamp = (min, max, value) => value <= min ? min : value >= max ? max : value;
  const unobserve = observeViewport((top, bottom) => {
    const bounds = chart.getBoundingClientRect();
    const height = chart.viewBox.baseVal.height;
    const scale = height / bounds.height;
    const offset = top - bounds.top + topOffset;
    
    const axis = chart.querySelector(axisSelector);
    axis.setAttribute('transform', `translate(0, ${clamp(30, scale * (height - topOffset), scale * offset)})`);
  });
  invalidation.then(unobserve);
}

```

This will modify the `transform` attribute of your axis group so that it remains within the viewport.

---

<div class="post-metadata">

**Author:** ![jcweaver](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/jcweaver/32/6091_2.png) [@jcweaver](https://talk.observablehq.com/u/jcweaver)\
**Post date:** [February 9, 2022, 6:25pm UTC](https://talk.observablehq.com/t/how-to-make-an-x-axis-sticky-on-a-d3-chart/6177/5 "2022-02-09T18:25:54Z")

</div>

Thanks so much for the pointers and recommendations!
