# How to remove the top and bottom ticks of the axes

**URL:** <https://talk.observablehq.com/t/how-to-remove-the-top-and-bottom-ticks-of-the-axes/6509>\
**Category:** Help\
**Created:** [May 2, 2022, 12:39pm UTC](https://talk.observablehq.com/t/how-to-remove-the-top-and-bottom-ticks-of-the-axes/6509 "2022-05-02T12:39:22Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![smpa01](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/smpa01/32/5352_2.png) [@smpa01](https://talk.observablehq.com/u/smpa01)\
**Post date:** [May 2, 2022, 12:39pm UTC](https://talk.observablehq.com/t/how-to-remove-the-top-and-bottom-ticks-of-the-axes/6509/1 "2022-05-02T12:39:23Z")

</div>

I am trying to figure out how can I customise an axis as per my req.

For example, the following code ([an example notebook](https://observablehq.com/@smpa01/style-axes))

```auto
  const tbl = [{ "Month": 1, "Value": 14841 }, { "Month": 2, "Value": 24467 }, { "Month": 3, "Value": 78423 }, { "Month": 4, "Value": 60213 }, { "Month": 5, "Value": 87257 }, { "Month": 6, "Value": 21543 }, { "Month": 7, "Value": 21373 }, { "Month": 8, "Value": 87363 }, { "Month": 9, "Value": 50378 }, { "Month": 10, "Value": 29714 }, { "Month": 11, "Value": 20171 }, { "Month": 12, "Value": 70059 }]

//the chart X Axis needs to start from 0
tbl.filter(a => (a.Month == 0)).length == 0 ? tbl.unshift({ "Month": 0, "Value": 0 }) : tbl

const width = 1280;
const height = 720;

//*-------2. DRAW CHART STRUCTURE (SVG HEIGHT,WIDTH, VBOX ETC)----------*//      
const glblDim = {
    height: height,
    width: width,
    margin: {
        top: 20,
        right: 20,
        bottom: 30,
        left: 50
    }
}
glblDim.boundedWidth = glblDim.width - glblDim.margin.right - glblDim.margin.left;
glblDim.boundedHeight = glblDim.height - glblDim.margin.top - glblDim.margin.bottom;

//namespace
const svgns = 'http://www.w3.org/2000/svg'
const body = document.querySelector('body')
const svg = document.createElementNS(svgns, 'svg');
svg.setAttribute('xmlns', svgns);
svg.setAttribute('viewBox', `0 0 ${width} ${height}`)
body.append(svg);

const bound = document.createElementNS(svgns, 'g');
bound.setAttribute('class', 'bound');
bound.style.setProperty('transform', `translate(${glblDim.margin.left}px, ${glblDim.margin.top}px`)
svg.appendChild(bound);

//*--------------------------3. SCALE----------------------------------*//  
//generate range X
const rangeX = d3.scaleTime().range([0, glblDim.boundedWidth]);

//generate scale first X 
var scaleX = rangeX
    .domain(d3.extent(tbl, d => d.Month)) 

//generate rangeY
const rangeY = d3.scaleLinear().range([glblDim.boundedHeight, 0]);

//generate scale first Y
var scaleY = rangeY
    .domain(d3.extent(tbl, d => d.Value)) 

//-----------------4.AXES----------------------------------------------//     
//generate y Axis
d3.select(bound).append('g')
    .call(d3.axisLeft(scaleY))
    .attr('class', 'YAxis')
    .call(a => a.selectAll(".tick")
        .remove())

```

produces this

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/f/fde6bea739a08c1c2ef31ec497001187760c44f0.png)  
The markup is following  
 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/7/707b41ec3b74dbf9a1bd5a41002404ec56d73158.png)

but what I really want is the following  
 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/7/7334e660ce3c9d2ecb3a59b1c5b40bad8e49b93b.png)

which I can achieve by manually editing the path markup as following

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

Is there any way within d3 to get rid of those top and bottom lines of the axis path while generating the axis?

---

<div class="post-metadata">

**Author:** ![mcmcclur](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mcmcclur/32/2364_2.png) [@mcmcclur](https://talk.observablehq.com/u/mcmcclur)\
**Post date:** [May 2, 2022, 2:49pm UTC](https://talk.observablehq.com/t/how-to-remove-the-top-and-bottom-ticks-of-the-axes/6509/2 "2022-05-02T14:49:53Z")

</div>

It looks to me like you’ve studied some material that describes the use of D3 in the browser context and are trying to apply those techniques within Observable notebooks. The two environments are really quite different, though, and the techniques you use at the `document` level are much more cumbersome than necessary. Observable is definitely an outstanding choice for the types of charts you want to generate but, if you’re interested in using it, I recommend that you [read the documentation](https://observablehq.com/@observablehq/documentation) thoroughly.

To create an SVG, you might do something like the following:

```
{
  let svg = d3.create('svg')
    .attr(...)
  ...
  return svg.node()
}

```

No need create namespaces or append object to body or any other manipulation at the document level.

More specific to the task at hand, you might do something like the following:

```
{
  let w = 500;
  let h = 300;
  let pad = 30;
  let svg = d3.create("svg").attr("viewBox", `0 0 ${w} ${h}`);
  let axes_group = svg.append("g");

  let x_scale = d3.scaleLinear()
    .domain([0, 5]).range([pad, w - pad]);
  let y_scale = d3.scaleLinear()
    .domain([0, 3]).range([h - pad, pad]);

  svg.append("g")
    .attr("transform", `translate(0, ${h - pad})`)
    .call(d3.axisBottom(x_scale));
  svg.append("g")
    .attr("transform", `translate(${pad})`)
    .call(d3.axisLeft(y_scale).tickValues([]).tickSize(0));

  return svg.node();
}

```

Note that I used [`axis.tickSize`](https://github.com/d3/d3-axis#axis_tickSize) to remove the short segments you didn’t want. The result looks like so:

 ![no_tick](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/e/e9a4216d29848eee3d1c122ab417dc2bfe9adace.png)

---

<div class="post-metadata">

**Author:** ![smpa01](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/smpa01/32/5352_2.png) [@smpa01](https://talk.observablehq.com/u/smpa01)\
**Post date:** [May 2, 2022, 3:44pm UTC](https://talk.observablehq.com/t/how-to-remove-the-top-and-bottom-ticks-of-the-axes/6509/3 "2022-05-02T15:44:25Z")

</div>

Thanks @mcmcclur for the response.

You are right, that I am trying to figure out both d3 (first priority) and observable at the same time. However, my work will be consumed via a browser. Therefore, I only use Observable notebook to present an example notebook here for my question. I guess it will take me a while to seamlessly use Observable.

Coming back to the question. I tried your solution and it looks like `tickValues([]).tickSize(0)` will get rid of all the ticks and their corresponding values, which I don’t want.

To be able to solve my problem, I wrote a javascript like this, but I am not sure if that is foolproof. The code gets the `d` attribute of the `xAxis` and `yAxis` path and then I use a regex to manipulate that to my desire outcome.

I will totally avoid that if there is an in-built d3 way to do this.

```auto
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Document</title>
</head>

<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>   
  
<script>
const tbl = [{ "Month": 1, "Value": 14841 }, { "Month": 2, "Value": 24467 }, { "Month": 3, "Value": 78423 }, { "Month": 4, "Value": 60213 }, { "Month": 5, "Value": 87257 }, { "Month": 6, "Value": 21543 }, { "Month": 7, "Value": 21373 }, { "Month": 8, "Value": 87363 }, { "Month": 9, "Value": 50378 }, { "Month": 10, "Value": 29714 }, { "Month": 11, "Value": 20171 }, { "Month": 12, "Value": 70059 }]

//the chart X Axis needs to start from 0
tbl.filter(a => (a.Month == 0)).length == 0 ? tbl.unshift({ "Month": 0, "Value": 0 }) : tbl

const width = 1280;
const height = 720;

//*-------2. DRAW CHART STRUCTURE (SVG HEIGHT,WIDTH, VBOX ETC)----------*//      
const glblDim = {
    height: height,
    width: width,
    margin: {
        top: 20,
        right: 20,
        bottom: 30,
        left: 50
    }
}
glblDim.boundedWidth = glblDim.width - glblDim.margin.right - glblDim.margin.left;
glblDim.boundedHeight = glblDim.height - glblDim.margin.top - glblDim.margin.bottom;

//namespace
const svgns = 'http://www.w3.org/2000/svg'

const svg = d3.select('body').append('svg').attr('xmlns', svgns)
    .attr("viewBox", `0 0 ${width} ${height}`);
const bound = svg.append('g').attr('class', 'bound') //this is the inner portion where the drawing gets made
    .style('transform', `translate(${glblDim.margin.left}px, ${glblDim.margin.top}px)`)

//*--------------------------3. SCALE----------------------------------*//  
//generate range X
const rangeX = d3.scaleLinear().range([0, glblDim.boundedWidth]);

//generate scale first X 
var scaleX = rangeX
    .domain(d3.extent(tbl, d => d.Month))

//generate rangeY
const rangeY = d3.scaleLinear().range([glblDim.boundedHeight, 0]);

//generate scale first Y
var scaleY = rangeY
    .domain(d3.extent(tbl, d => d.Value))

//-----------------4.AXIS----------------------------------------------//     
//generate Y Axis
bound.append('g')
    .call(d3.axisLeft(scaleY))
    .attr('class', 'YAxis')
    .call(d => d.select('.domain') //change Axis class name
        .attr('class', 'yAxis'))
    .call(d => d.selectAll('.tick') //change the tick class name
        .attr('class', (d, i) => { return `yAxisTick${[i]}` }))
    .call(d => d.select('.yAxisTick0') //chnage the opacity of the first tick to 0
        .attr('opacity', 0))

//format Y Axis Path to get rid rid of bottom and top axis ticks
const getYAxis = d3.select('.yAxis')
const dStringY = getYAxis.attr('d').replace(/^(M-\d+|M\d+)/gm, 'M0').replace(/(H-\d+|H\d+)$/gm, '')
getYAxis.attr('d', dStringY);

//generate X Axis
bound.append('g')
    .call(d3.axisBottom(scaleX))
    .attr('class', 'XAxis')
    .style('transform', `translateY(${glblDim.boundedHeight}px)`)
    .call(d => d.select('.domain') //change Axis class name
        .attr('class', 'xAxis'))
    .call(d => d.selectAll('.tick') //change the tick class name
        .attr('class', (d, i) => { return `xAxisTick${[i]}` }))
    .call(d => d.select('.xAxisTick0') //chnage the opacity of the first tick to 0
        .attr('opacity', 0))
    // .call(a => a.selectAll("[class^='xAxisTick']")
    // .remove())

//format X Axis Path to get rid rid of bottom and top axis ticks
const getXAxis = d3.select('.xAxis')
const dStringX = getXAxis.attr('d').replace(/,\d+V/gm, ',0').replace(/(V-\d+|V\d+)$/gm, '')
getXAxis.attr('d', dStringX);
</script>

</body>

</html>

```

generates this (which I want)

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/0/0c558efa09ebe2c24f0b14b4fce639dd358ca84a.png)  
which was initially this without the `path` manipulation (which I don’t want)  
 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/2/2d888eebe75145e89be7fcfe99db1ade772983f1.png)

Thank you in advance.

---

<div class="post-metadata">

**Author:** ![Fil](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/fil/32/207_2.png) [@Fil](https://talk.observablehq.com/u/Fil)\
**Post date:** [May 2, 2022, 4:36pm UTC](https://talk.observablehq.com/t/how-to-remove-the-top-and-bottom-ticks-of-the-axes/6509/4 "2022-05-02T16:36:06Z")

</div>

This would be the [tickSizeOuter](https://github.com/d3/d3-axis#axis_tickSizeOuter) setting.

> d3.axisBottom(x).tickSizeOuter(0)

---

<div class="post-metadata">

**Author:** ![smpa01](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/smpa01/32/5352_2.png) [@smpa01](https://talk.observablehq.com/u/smpa01)\
**Post date:** [May 2, 2022, 4:52pm UTC](https://talk.observablehq.com/t/how-to-remove-the-top-and-bottom-ticks-of-the-axes/6509/5 "2022-05-02T16:52:45Z")

</div>

@Fil I tried what you suggested but it does not give me what I need  
 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/f/f96224cd9fb09935e736501c811a9ea5d06ac28e.png)

```auto
bound.append('g')
    .call(d3.axisBottom(scaleX).tickSizeOuter(0))
    .attr('class', 'XAxis')
    .style('transform', `translateY(${glblDim.boundedHeight}px)`)

```

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/6/62ba7263da2ae9ea1852c248e4108264cf076f56.png)

---

<div class="post-metadata">

**Author:** ![mcmcclur](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mcmcclur/32/2364_2.png) [@mcmcclur](https://talk.observablehq.com/u/mcmcclur)\
**Post date:** [May 2, 2022, 4:57pm UTC](https://talk.observablehq.com/t/how-to-remove-the-top-and-bottom-ticks-of-the-axes/6509/6 "2022-05-02T16:57:21Z")

</div>

> [@smpa01](#):
>
> I tried your solution and it looks like `tickValues([]).tickSize(0)` will get rid of all the ticks and their corresponding values, which I don’t want.

Then you should call `.tickSizeOuter`:

```
{
  let w = 500;
  let h = 300;
  let pad = 30;
  let svg = d3.create("svg").attr("viewBox", `0 0 ${w} ${h}`);
  let axes_group = svg.append("g");

  let x_scale = d3.scaleLinear()
    .domain([0, 5]).range([pad, w - pad]);
  let y_scale = d3.scaleLinear()
    .domain([0, 3.141]).range([h - pad, pad]);

  svg
    .append("g")
    .attr("transform", `translate(0, ${h - pad})`)
    .call(d3.axisBottom(x_scale));
  svg
    .append("g")
    .attr("transform", `translate(${pad})`)
    .call(d3.axisLeft(y_scale).tickSizeOuter(0));

  return svg.node();
}

```

 ![temp](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/7/7500ebf367d9e1119d3c089f65cc540bea8c34a6.png)

---

<div class="post-metadata">

**Author:** ![mcmcclur](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mcmcclur/32/2364_2.png) [@mcmcclur](https://talk.observablehq.com/u/mcmcclur)\
**Post date:** [May 2, 2022, 5:15pm UTC](https://talk.observablehq.com/t/how-to-remove-the-top-and-bottom-ticks-of-the-axes/6509/7 "2022-05-02T17:15:55Z")

</div>

Here’s your chart, with the ticks fixed using `.tickSizeOuter` written in a somewhat more idiomatic way:

https://observablehq.com/embed/11571a315c50df5c?cells=chart_axes

I would also mention that this style is largely perfectly appropriate for working within the context of the brower. Once you have the webpage setup with D3 included as a script and a `DIV` with an id, simply change

```
d3.create('svg')

```

to

```
d3.select('#div_id').append('svg')

```

Of course, the return statement is no longer necessary but that’s it.
