Why using d3 in conjunction with vanilla generates an error?

I am currently trying to learn d3 and experimenting with using vanilla and d3 in the same script. I am somewhat fluent in vanilla to create svg. As I am learning d3, I am trying to write vanilla for the portion I still haven’t figured out the d3 portion and for the portions, I figured out, I am writing in d3.

For example, the following d3 script does what is expected

	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')
		.classed("layer1", true)
		.attr('xmlns', svgns)
		.attr('viewBox', `0 0 ${width} ${height}`)


	const bound = svg.append('g').attr('class', 'bound') 
		.style('transform', `translate(${glblDim.margin.left}px, ${glblDim.margin.top}px)`)

	//*--------------------------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)) //alternative  d => { return d.date }

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

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

	//-----------------4.AXES----------------------------------------------//     
	//generate x Axis
	bound.append('g')
		.call(d3.axisLeft(scaleY))
		.attr('class', 'YAxis')

However, if I use vanilla to create the svg and use d3 to create scale and axis, it falls flat.

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;

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)) //alternative  d => { return d.date }

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

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

//-----------------4.AXES----------------------------------------------//     
//generate x Axis
bound.append('g')
    .call(d3.axisLeft(scaleY))
    .attr('class', 'YAxis')

I am completely surprised by this. Can someone please help explain why the error is happening and how can I debug this.

I created an example notebook

I plan to keep on using vanilla and d3 in the same script seamlessly till I become 100% confident with d3.

How can I keep on doing that?

Thank you in advance.

your notebook is unpublished; but I think what’s missing here is that you should use d3.select(bound).append('g') instead of bound.append('g'); since bound is a DOM node it has no append prop.

1 Like

Thanks @Fil for the quick rescue

And since you’re already using D3, you may just want to create your elements in the same way:

const svg = d3.create('svg');
// ...
const bound = d3.create('svg:g');

Read more about d3.create here.

2 Likes