效果是将开启了换行的列文本限制为显示前 3 行(这个数值可以改)→ 靠 CSS 实现
如果文本超出 3 行,鼠标悬浮在单元格上时可以在悬浮提示中看到完整的文本 → 靠 JS 实现
// 限制数据库换行文本最大行数(不包括主键换行) JS片段 - author: JeffreyChen
var animationFrameRequestId = null; // 用于存储 requestAnimationFrame 的 ID
function updateAriaLabels() { // 数据库渲染后所有 aria-label 属性都会丢失,所以直接全部添加即可
// 如果已经有一个动画帧请求在等待,取消它
if (animationFrameRequestId !== null) {
cancelAnimationFrame(animationFrameRequestId);
}
animationFrameRequestId = requestAnimationFrame(function() {
document.querySelectorAll('.av__row:not(.av__row--header) .av__cell[data-wrap="true"]:not([data-block-id])').forEach(cell => {
const textElement = cell.querySelector('.av__celltext'); // 查找包含文本的子元素
// 检查父元素是否已有 aria-label 属性、是否是有效的 DOM 元素、文本是否被截断
if (!cell.getAttribute('aria-label') && textElement && textElement.scrollHeight > textElement.clientHeight) {
const text = textElement.textContent.trim(); // 提取文本
cell.setAttribute('aria-label', text); // 为单元格添加 aria-label 属性
}
});
// 重置 animationFrameRequestId,以便下次调用 updateAriaLabels 时可以检查
animationFrameRequestId = null;
});
}
function updateAriaLabels2() { // 调整列(宽)后 aria-label 仍然保留,需要逐个判断移除或者添加
// 如果已经有一个动画帧请求在等待,取消它
if (animationFrameRequestId !== null) {
cancelAnimationFrame(animationFrameRequestId);
}
animationFrameRequestId = requestAnimationFrame(function() {
document.querySelectorAll('.av__row:not(.av__row--header) .av__cell[data-wrap="true"]:not([data-block-id])').forEach(cell => {
const textElement = cell.querySelector('.av__celltext'); // 查找包含文本的子元素
// 检查父元素是否已有 aria-label 属性、是否是有效的 DOM 元素、文本是否被截断
if (cell.getAttribute('aria-label') && textElement && !(textElement.scrollHeight > textElement.clientHeight)) {
cell.removeAttribute('aria-label'); // 为无截断文本的单元格移除 aria-label 属性
} else if (!cell.getAttribute('aria-label') && textElement && textElement.scrollHeight > textElement.clientHeight) {
const text = textElement.textContent.trim(); // 提取文本
cell.setAttribute('aria-label', text); // 为有截断文本单元格添加 aria-label 属性
}
});
// 重置 animationFrameRequestId,以便下次调用 updateAriaLabels 时可以检查
animationFrameRequestId = null;
});
}
var timeoutId = null;
// 创建一个新的 MutationObserver 实例,并提供一个回调函数
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes') {
// 数据库渲染:检查被修改的节点是否是数据库 av 类型并且已经渲染完成
if (mutation.target.classList.contains('av') && mutation.target.getAttribute('data-render') === 'true') {
clearTimeout(timeoutId);
timeoutId = setTimeout(function() {
updateAriaLabels()
}, 500); // 500 毫秒延时。用以避免短时间内重复执行
// 调整列(宽):检查被修改的节点是否是数据库列头并且开启了换行
} else if (mutation.target.classList.contains('av__cell--header') && mutation.target.getAttribute('data-wrap') === 'true') {
clearTimeout(timeoutId);
timeoutId = setTimeout(function() {
updateAriaLabels2()
}, 500); // 500 毫秒延时。拖拽的过程中属性会高频变化,此时不继续运行
}
}
});
});
// 配置MutationObserver以观察DOM树的变化
const config = { attributes: true, childList: false, subtree: true };
// 开始观察
observer.observe(document.body, config);
// 创建一个新的style元素
var style = document.createElement('style');
// 添加CSS代码
style.textContent = `
.av__row:not(.av__row--header) .av__cell[data-wrap="true"]:not([data-block-id]) .av__celltext {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3; /* 最多3行 */
overflow: hidden;
}
`;
// 将style元素添加到文档的head中
document.head.appendChild(style);
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于