document.addEventListener("DOMContentLoaded", () => {
const credentials = document.querySelectorAll(".credential li");
const careers = document.querySelectorAll(".career li");
const table = document.querySelector(".table-container");
function resetUI() {
// Reset all elements
credentials.forEach((c) => {
c.classList.remove("selected");
c.classList.remove("disabled");
});
careers.forEach((c) => {
c.classList.remove("selected");
c.classList.remove("disabled");
});
// Clear all arrows from the SVG
const svg = document.getElementById("arrows");
while (svg.firstChild) {
svg.removeChild(svg.firstChild);
}
// Update the SVG dimensions to cover the entire table
const tableRect = table.getBoundingClientRect();
svg.setAttribute("width", tableRect.width);
svg.setAttribute("height", tableRect.height);
// Re-append the arrowhead marker
const marker = document.createElementNS(
"http://www.w3.org/2000/svg",
"marker"
);
marker.setAttribute("id", "arrowhead");
marker.setAttribute("markerWidth", "10");
marker.setAttribute("markerHeight", "7");
marker.setAttribute("refX", "0");
marker.setAttribute("refY", "3.5");
marker.setAttribute("orient", "auto");
const arrowhead = document.createElementNS(
"http://www.w3.org/2000/svg",
"polygon"
);
arrowhead.setAttribute("points", "0 0, 10 3.5, 0 7");
arrowhead.setAttribute("fill", "orange");
marker.appendChild(arrowhead);
svg.appendChild(marker);
}
function drawArrowFromCertificateToCareer(fromElement, toElement) {
const svg = document.getElementById("arrows");
const fromRect = fromElement.getBoundingClientRect();
const toRect = toElement.getBoundingClientRect();
const svgRect = svg.getBoundingClientRect();
// Calculate positions relative to the table container
const fromX = fromRect.right - svgRect.left; // Right side of the certificate
const fromY = fromRect.top + fromRect.height / 2 - svgRect.top; // Middle of the certificate
const toX = toRect.left - svgRect.left; // Left side of the career
const toY = toRect.top + toRect.height / 2 - svgRect.top; // Middle of the career
const arrow = document.createElementNS("http://www.w3.org/2000/svg", "line");
arrow.setAttribute("x1", fromX);
arrow.setAttribute("y1", fromY);
arrow.setAttribute("x2", toX);
arrow.setAttribute("y2", toY);
arrow.setAttribute("stroke", "orange");
arrow.setAttribute("stroke-width", "1.0");
arrow.setAttribute("marker-end", "url(#arrowhead)");
svg.appendChild(arrow);
}
function drawCircularArrows(fromCareer, credentialIds) {
const svg = document.getElementById("arrows");
const svgRect = svg.getBoundingClientRect();
const careerRect = fromCareer.getBoundingClientRect();
// Sort credentials by their vertical position
const sortedCredentials = credentialIds
.map((id) => document.getElementById(id))
.filter((el) => el !== null)
.sort(
(a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top
);
// Draw arrows between credentials
for (let i = 0; i < sortedCredentials.length - 1; i++) {
const fromRect = sortedCredentials[i].getBoundingClientRect();
const toRect = sortedCredentials[i + 1].getBoundingClientRect();
const arrow = document.createElementNS(
"http://www.w3.org/2000/svg",
"line"
);
arrow.setAttribute(
"x1",
fromRect.left - svgRect.left + fromRect.width / 2
);
arrow.setAttribute("y1", fromRect.bottom - svgRect.top);
arrow.setAttribute("x2", toRect.left - svgRect.left + toRect.width / 2);
arrow.setAttribute("y2", toRect.top - svgRect.top);
arrow.setAttribute("stroke", "orange");
arrow.setAttribute("stroke-width", "2");
arrow.setAttribute("marker-end", "url(#arrowhead)");
svg.appendChild(arrow);
}
// Draw arrow from last credential to career
if (sortedCredentials.length > 0) {
const lastCredRect =
sortedCredentials[sortedCredentials.length - 1].getBoundingClientRect();
const arrow = document.createElementNS(
"http://www.w3.org/2000/svg",
"line"
);
arrow.setAttribute(
"x1",
lastCredRect.left - svgRect.left + lastCredRect.width / 2
);
arrow.setAttribute("y1", lastCredRect.bottom - svgRect.top);
arrow.setAttribute("x2", careerRect.left - svgRect.left); // Point to the left edge of the career span
arrow.setAttribute(
"y2",
careerRect.top - svgRect.top + careerRect.height / 2
); // Point to the vertical middle of the career span
arrow.setAttribute("stroke", "orange");
arrow.setAttribute("stroke-width", "2");
arrow.setAttribute("marker-end", "url(#arrowhead)");
svg.appendChild(arrow);
}
}
function highlightRelatedItems(selectedItem, _items, dataAttribute) {
const relatedItems = selectedItem.dataset[dataAttribute].split(",");
// Highlight related items
relatedItems.forEach((itemId) => {
const itemElement = document.getElementById(itemId);
if (itemElement) {
itemElement.classList.remove("disabled");
itemElement.classList.add("selected");
if (selectedItem.closest(".credential")) {
// Draw arrow from credential to career
drawArrowFromCertificateToCareer(selectedItem, itemElement);
} else {
// Draw circular arrows for careers
drawCircularArrows(selectedItem, relatedItems);
}
}
});
}
// Add event listeners to credentials (Certificate, Degree or Credential column)
credentials.forEach((credential) => {
credential.addEventListener("click", (event) => {
event.stopPropagation(); // Prevent click event from propagating to the document
// Reset all elements
resetUI();
// Highlight the selected credential and disable others
credential.classList.add("selected");
credentials.forEach((c) => {
if (c !== credential) {
c.classList.add("disabled");
}
});
// Highlight related careers and draw arrows from credential to career
highlightRelatedItems(credential, careers, "related");
// Disable unselected careers
careers.forEach((c) => {
if (!c.classList.contains("selected")) {
c.classList.add("disabled");
}
});
});
});
// Add event listeners to careers
careers.forEach((career) => {
career.addEventListener("click", (event) => {
event.stopPropagation(); // Prevent click event from propagating to the document
// Reset all elements
resetUI();
// Highlight the selected career and disable others
career.classList.add("selected");
careers.forEach((c) => {
if (c !== career) {
c.classList.add("disabled");
}
});
// Retrieve related credentials from the data-related attribute
const relatedCredentials = career.dataset.related.split(",");
// Draw the circular arrows from career to credentials and back
drawCircularArrows(career, relatedCredentials);
// Highlight related credentials
highlightRelatedItems(career, credentials, "related");
// Disable unrelated credentials
credentials.forEach((c) => {
if (!c.classList.contains("selected")) {
c.classList.add("disabled");
}
});
});
});
document.addEventListener("click", resetUI);
window.addEventListener("resize", resetUI);
});
/* Improved typography and color scheme */
body {
font-family: 'Roboto', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
margin: 0;
padding: 20px;
}
/* Container with rounded corners and subtle shadow */
.table-container {
max-width: 1000px;
margin: 0 auto;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 24px;
position: relative;
/* For positioning the arrows SVG */
overflow-x: auto;
}
/* Table styling with alternating row colors */
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin-bottom: 20px;
overflow-x: auto;
}
th,
td {
padding: 12px 16px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
table thead tr th:first-child {
width: 20%;
}
table thead tr th:nth-child(2),
table tbody tr th:nth-child(3) {
width: 40%;
}
th {
background-color: #f0f4f8;
font-weight: 600;
color: #2c3e50;
text-transform: uppercase;
font-size: 0.9em;
letter-spacing: 0.5px;
}
tr:nth-child(even) {
background-color: #f9fafb;
}
/* Rounded corners for first and last cells in a row */
tr:first-child th:first-child {
border-top-left-radius: 8px;
}
tr:first-child th:last-child {
border-top-right-radius: 8px;
}
tr:last-child td:first-child {
border-bottom-left-radius: 8px;
}
tr:last-child td:last-child {
border-bottom-right-radius: 8px;
}
/* Remove list-style and add styling for credential and career items */
.credential ul,
.career ul {
list-style-type: none;
padding: 0;
margin: 0;
}
.credential li,
.career li {
transition: all 0.3s ease;
padding: 8px 12px;
margin: 4px 0;
border-radius: 6px;
cursor: pointer;
}
.credential li:hover,
.career li:hover {
background-color: #e8f0fe;
transform: translateY(-2px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* Selected state with color highlight */
.selected {
background-color: #3498db;
color: white;
font-weight: 500;
}
/* Disabled state */
.disabled {
opacity: 0.5;
pointer-events: none;
}
/* Education level styling */
.education-level {
font-weight: 600;
padding: 12px;
border-radius: 6px;
}
/* Color scheme for education levels */
.high-school {
background-color: #e8eaf6;
}
.one-year-college {
background-color: #fff3e0;
}
.two-years-college {
background-color: #e0f2f1;
}
.four-years-college {
background-color: #fce4ec;
}
.six-years-college {
background-color: #e1f5fe;
}
.six-plus-years-college {
background-color: #fff8e1;
}
/* Arrows SVG styling */
#arrows {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
/* Responsive design */
@media (max-width: 768px) {
.table-container {
padding: 16px;
}
table {
min-width: 400px;
}
table thead tr th:nth-child(2),
table tbody tr th:nth-child(3) {
width: 46%;
}
table tbody tr td:first-child {
padding: 84px 0px;
text-align: center;
border-radius: 0;
}
table tbody tr td:first-child span {
transform: rotate(270deg);
white-space: nowrap;
/* Prevent the text from wrapping */
display: inline-block;
text-align: left;
/* Adjust alignment */
}
table td,
table th {
font-size: 12px;
}
table tbody tr td:nth-child(2),
table tbody tr td:nth-child(3) {
vertical-align: top;
}
th,
td {
padding: 8px;
}
td:before {
content: attr(data-label);
position: absolute;
left: 6px;
width: 45%;
padding-right: 10px;
white-space: nowrap;
font-weight: bold;
}
.credential li,
.career li {
padding: 6px 8px;
margin: 2px 0;
}
#arrows line {
stroke-width: 0.5;
/* Thinner lines for small screens */
}
/* Adjust the size of arrowheads */
marker polygon {
points: "0 0, 5 1.75, 0 3.5";
/* Smaller arrowhead size */
}
}
@media (hover: none) {
.credential li,
.career li {
padding: 12px 8px;
}
}
/* Smooth transitions for all interactive elements */
a,
button,
input,
select,
textarea {
transition: all 0.3s ease;
}
/* Optimize white space */
li {
margin-bottom: 8px;
}
<main>
<div class="table-container" role="table" aria-label="Career Pathways Table">
<table>
<thead>
<tr>
<th scope="col">Education Level</th>
<th scope="col">Certificate, Degree or Credential</th>
<th scope="col">Career</th>
</tr>
</thead>
<tbody>
<tr>
<td class="education-level high-school"><span>High School</span></td>
<td class="credential">
<ul>
<li id="high-school-diploma" data-related="assistant,technician,specialist">High School Diploma
</li>
<li id="vocational-certificate" data-related="assistant">Vocational Certificate</li>
</ul>
</td>
<td class="career">
<ul>
<li id="entry-level" data-related="associate-degree,certificate,vocational-certificate,phd-science">
Entry-level positions</li>
<li id="assistant" data-related="high-school-diploma,vocational-certificate">Assistant roles
</li>
</ul>
</td>
</tr>
<tr>
<td class="education-level one-year-college"><span>1 Year College</span></td>
<td class="credential">
<ul>
<li id="certificate" data-related="assistant,technician">Certificate</li>
<li id="technical-certificate" data-related="technician">Technical Certificate</li>
</ul>
</td>
<td class="career">
<ul>
<li id="assistant" data-related="certificate,technical-certificate">Assistant roles</li>
<li id="technician" data-related="certificate,technical-certificate">Technician roles
</li>
</ul>
</td>
</tr>
<tr>
<td class="education-level two-years-college"><span>2 Years College</span></td>
<td class="credential">
<ul>
<li id="associate-degree" data-related="professional,specialist">Associate Degree</li>
<li id="diploma-technology" data-related="professional,specialist">Diploma in Technology
</li>
</ul>
</td>
<td class="career">
<ul>
<li id="professional" data-related="associate-degree,diploma-technology">Professional roles
</li>
<li id="specialist" data-related="associate-degree,diploma-technology">Specialist roles
</li>
</ul>
</td>
</tr>
<tr>
<td class="education-level four-years-college"><span>4 Years College</span></td>
<td class="credential">
<ul>
<li id="bachelors-degree" data-related="specialist,manager">Bachelor's Degree</li>
<li id="bachelor-arts" data-related="manager">Bachelor of Arts</li>
</ul>
</td>
<td class="career">
<ul>
<li id="specialist" data-related="bachelors-degree,bachelor-arts">Specialist roles</li>
<li id="manager" data-related="bachelors-degree,bachelor-arts">Management roles</li>
</ul>
</td>
</tr>
<tr>
<td class="education-level six-years-college"><span>6 Years College</span></td>
<td class="credential">
<ul>
<li id="masters-degree" data-related="expert,consultant">Master's Degree</li>
<li id="mba" data-related="expert,consultant">MBA</li>
</ul>
</td>
<td class="career">
<ul>
<li id="expert" data-related="masters-degree,mba">Expert roles</li>
<li id="consultant" data-related="masters-degree,mba">Consultant roles</li>
</ul>
</td>
</tr>
<tr>
<td class="education-level six-plus-years-college"><span>6+ Years College</span></td>
<td class="credential">
<ul>
<li id="doctorate" data-related="expert,researcher">Doctorate</li>
<li id="phd-science" data-related="expert,researcher">PhD in Science</li>
</ul>
</td>
<td class="career">
<ul>
<li id="expert" data-related="associate-degree,bachelors-degree,doctorate">Expert roles
</li>
<li id="researcher" data-related="doctorate,phd-science">Research roles</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
</main>
<svg id="arrows" aria-hidden="true"></svg>