Skip to content

Commit e5806a7

Browse files
Merge pull request #1536 from ASU/UDS-2010
UDS-2010: feat(unity-bootstrap-theme): added js for trucate aria-describedby on…
2 parents 4b7e643 + 0524735 commit e5806a7

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { EventHandler } from "./bootstrap-helper";
2+
3+
/**
4+
* Initializes the card body elements by applying text truncation if the content exceeds
5+
* a specified height based on the CSS line-clamp and line-height properties. Adds
6+
* accessible text for truncated content.
7+
*
8+
* @return {void} This function does not return any value.
9+
*/
10+
function initCardBodies() {
11+
12+
const cardBodies = document.querySelectorAll('.card-body');
13+
cardBodies.forEach((cardBody, index) => {
14+
const paragraph = cardBody.querySelector('div p');
15+
const originalText = paragraph.textContent;
16+
const style = window.getComputedStyle(cardBody);
17+
18+
// Get the number of lines allowed (line clamp), usually set via CSS like -webkit-line-clamp
19+
const lineClamp = parseInt(style.webkitLineClamp || style.lineClamp);
20+
21+
// Get the line height and font size from the styles
22+
const lineHeight = parseFloat(style.lineHeight);
23+
const fontSize = parseFloat(style.fontSize);
24+
25+
// Calculate the actual line height. If lineHeight is not a number, estimate using fontSize
26+
const actualLineHeight = isNaN(lineHeight) ? parseFloat(style.lineHeight) * fontSize : lineHeight;
27+
28+
// Calculate the maximum height allowed for the paragraph (lines × line height)
29+
const maxHeight = lineClamp * actualLineHeight;
30+
31+
// Check if the paragraph exceeds the maximum allowed height
32+
if (paragraph.offsetHeight >= maxHeight) {
33+
let visibleText = '';
34+
const words = originalText.split(' ');
35+
let visibleWordCount = 0;
36+
let tempText = '';
37+
38+
// Add words one by one until the text height exceeds the max allowed height
39+
while (visibleWordCount < words.length && getTextHeight(tempText + (tempText ? ' ' : '') + words[visibleWordCount], paragraph) <= maxHeight) {
40+
tempText += (tempText ? ' ' : '') + words[visibleWordCount];
41+
visibleWordCount++;
42+
}
43+
visibleText = tempText + '...';
44+
45+
// Create a new hidden element to store the truncated text
46+
const visibleTextElementId = `visible-text-${Math.random().toString(36).substring(7)}`;
47+
const visibleTextElement = document.createElement('div');
48+
visibleTextElement.id = visibleTextElementId;
49+
visibleTextElement.textContent = visibleText;
50+
visibleTextElement.style.position = 'absolute';
51+
visibleTextElement.style.top = `${paragraph.offsetTop}px`;
52+
visibleTextElement.style.left = `${paragraph.offsetLeft}px`;
53+
visibleTextElement.style.width = `${paragraph.offsetWidth}px`;
54+
visibleTextElement.style.height = `${paragraph.offsetHeight}px`;
55+
visibleTextElement.style.opacity = '0';
56+
visibleTextElement.style.pointerEvents = 'none';
57+
visibleTextElement.style.zIndex = '1';
58+
59+
// Add the hidden element to the DOM
60+
cardBody.appendChild(visibleTextElement);
61+
62+
paragraph.setAttribute('aria-describedby', visibleTextElementId);
63+
64+
// Hide the original paragraph from screen readers
65+
paragraph.setAttribute('aria-hidden', 'true');
66+
67+
}
68+
});
69+
70+
71+
}
72+
73+
/**
74+
* Calculates the rendered height of a given text when styled and constrained
75+
* within the dimensions and styles of the specified HTML element.
76+
*
77+
* @param {string} text - The text content whose height is to be measured.
78+
* @param {HTMLElement} element - The element used to derive styling and width constraints.
79+
* @return {number} The height in pixels of the rendered text.
80+
*/
81+
function getTextHeight(text, element) {
82+
const tempElement = document.createElement(element.tagName);
83+
tempElement.style.font = window.getComputedStyle(element).font;
84+
tempElement.style.width = window.getComputedStyle(element).width;
85+
tempElement.style.whiteSpace = 'pre-wrap';
86+
tempElement.textContent = text;
87+
document.body.appendChild(tempElement);
88+
const height = tempElement.offsetHeight;
89+
document.body.removeChild(tempElement);
90+
return height;
91+
}
92+
93+
EventHandler.on(window, 'load.uds.card-bodies', initCardBodies);
94+
95+
export { initCardBodies };

packages/unity-bootstrap-theme/src/js/unity-bootstrap.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { initAnchorMenu } from "./anchor-menu.js";
33
import { initBlockquoteAnimation } from "./blockquote-animated.js";
44
import { initCalendar } from "./calendar.js";
5+
import { initCardBodies } from "./card-bodies.js";
56
import { initRankingCard } from "./card-ranking.js";
67
import { initChart } from "./charts-and-graphs.js";
78
import { initDataLayer } from "./data-layer.js";
@@ -28,6 +29,7 @@ const unityBootstrap = {
2829
initRankingCard,
2930
initTabbedPanels,
3031
initVideo,
32+
initCardBodies,
3133
}
3234

3335
export default unityBootstrap;

0 commit comments

Comments
 (0)