Hey!
I’m new to 3djs and I’m having troubles with a rather silly thing.
If I use a barchart thats defined in the same file as my index.html, it works just fine.
This however does not look pretty and it bloats my code.
So I decided to move the function Barchart into a different file and import to my index.html.
This however does not work for some reason… what am I missing?
here is my code:
file named: (index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Visualization - Labwork Analysis</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
transition: background-color 0.3s, color 0.3s;
}
body.dark-mode {
background-color: black !important;
color: dimgray !important;
}
body.dark-mode a {
color: #999 !important;
}
body.dark-mode #chart-container {
border: 1px solid #444 !important;
}
button {
margin-bottom: 10px;
padding: 10px;
font-size: 16px;
cursor: pointer;
border: none;
border-radius: 5px;
background-color: #eee;
color: black;
transition: background-color 0.3s, color 0.3s;
}
button:hover {
background-color: #ddd;
}
.dark-mode button {
background-color: #333;
color: white;
}
#mode-toggle.dark-mode {
background-color: #333;
color: white;
}
#chart-container {
margin-top: 20px;
}
#chart-placeholder {
border: 1px solid #ccc;
padding: 20px;
text-align: center;
}
#dropdown-container {
margin-top: 10px;
}
select {
padding: 8px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 5px;
}
</style>
</head>
<body>
<h1 id="page-title">Labwork Analysis</h1>
<button id="query1-button">Applications vs. Finishes</button>
<button id="query2-button">Applicants & Participants per Milestone</button>
<button id="mode-toggle">Dark Mode</button>
<div id="dropdown-container">
<label for="labwork-dropdown">Select Milestone:</label>
<select id="labwork-dropdown">
<option value="">Select Labwork</option>
<option value="0">Labwork 1</option>
<option value="1">Labwork 2</option>
<option value="2">Labwork 3</option>
<option value="3">Labwork 4</option>
<option value="4">Labwork 5</option>
</select>
</div>
<div id="chart-container">
<div id="chart-placeholder">Select a query and labwork to display the chart.</div>
</div>
<script src="barChart.js"></script>
<script src="pieChart.js"></script>
<script>
const modeToggle = document.getElementById("mode-toggle");
modeToggle.addEventListener("click", toggleDarkMode);
function toggleDarkMode() {
document.body.classList.toggle("dark-mode");
modeToggle.textContent = document.body.classList.contains("dark-mode") ? "Light Mode" : "Dark Mode";
}
let index = 0;
document.getElementById('labwork-dropdown').addEventListener('change', () => updateLabworkUUID());
document.getElementById('query1-button').addEventListener('click', () => executeQuery(1));
document.getElementById('query2-button').addEventListener('click', () => executeQuery(2));
async function executeQuery(queryNumber) {
console.log('Query number: ', queryNumber);
const urls = [`http://localhost:3000/api/anmeldungen-vs-bestanden/${index}`,
`http://localhost:3000/api/anmeldungen-und-teilnahmen/${index}`];
const url = urls[queryNumber-1];
const response = await fetch(url);
if (!response.ok) {
console.error('Error fetching data:', response.statusText);
return;
}
const res = await response.json(); // Parse the JSON response
let resData = res.rows;
document.addEventListener('DOMContentLoaded', createBarChart)
document.addEventListener('DOMContentLoaded', createPieChart)
if (queryNumber === 1) {
console.log('Executing query number: ', queryNumber);
const chart = await createPieChart(resData);
await updateChart(chart);
} else if (queryNumber === 2) {
console.log('Executing query number: ', queryNumber);
const chart = await createBarChart(resData);
await updateChart(chart);
}
}
function updateLabworkUUID() {
index = document.getElementById('labwork-dropdown').value
console.log('Updating Labwork UUID');
}
async function updateChart(svg) {
console.log('Updating Chart...');
// Clear the placeholder content before rendering
const chartContainer = document.getElementById('chart-placeholder');
chartContainer.innerHTML = "";
console.log('Chart emptied...');
chartContainer.appendChild(svg);
console.log('Chart updated!');
}
</script>
</body>
</html>
and this is the file which I exported to a different file:
file named: (barChart.js)
//https://observablehq.com/@d3/horizontal-bar-chart/2?intent=fork
import {scaleLinear, max, scaleBand, create, axisLeft, axisTop} from "d3";
export function createBarChart(data) {
// Sort the data based on milestone_index in ascending order
data.sort((a, b) => a.milestone_index - b.milestone_index);
const barHeight = 100;
const marginTop = 0;
const marginRight = 30;
const marginBottom = 55;
const marginLeft = 150;
const width = 1500;
const height = 600;
// Create the scales.
const x = d3.scaleLinear()
.domain([0, d3.max(data, d => parseInt(d.total_entries))])
.range([marginLeft, width - marginRight]);
const y = d3.scaleBand()
.domain(data.map(d => d.milestone_index))
.rangeRound([marginTop, height - marginBottom])
.padding(0.1);
// Create the SVG container.
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 25px sans-serif;");
// Append a rect for each milestone.
svg.append("g")
.attr("fill", "steelblue")
.selectAll()
.data(data)
.join("rect")
.attr("x", x(0))
.attr("y", (d) => y(d.milestone_index))
.attr("width", (d) => x(parseInt(d.total_entries)) - x(0))
.attr("height", y.bandwidth());
// Append a label for each milestone.
svg.append("g")
.attr("fill", "white")
.attr("text-anchor", "end")
.selectAll()
.data(data)
.join("text")
.attr("x", (d) => x(parseInt(d.total_entries)))
.attr("y", (d) => y(d.milestone_index) + y.bandwidth() / 2)
.attr("dy", "0.35em")
.attr("dx", -4)
.text((d) => d.total_entries)
.call((text) => text.filter(d => x(parseInt(d.total_entries)) - x(0) < 20) // short bars
.attr("dx", +4)
.attr("fill", "black")
.attr("text-anchor", "start"));
// Create the axes.
svg.append("g")
.attr("transform", `translate(0,${height-marginBottom})`)
.call(d3.axisBottom(x).ticks(width / 80))
.call(g => g.select(".domain").remove());
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y).tickSizeOuter(0))
svg.append("text")
.attr("x", 0)
.attr("y", height/2)
.attr("fill", "#000")
.text("Milestones");
// X-axis label
svg.append("text")
.attr("x", width / 2)
.attr("y", height-10)
.attr("text-anchor", "middle")
.text("Students Participating");
svg.selectAll("text")
.attr("font-site", "50px")
.attr("fill", "#000")
.attr("font-weight", "bold")
return svg.node();
}
It doesnt throw any errors, it doesnt stop my program. Everything seems fine, except the graph simply doesnt show up.
Any help would be appreciated!