1

JavaScript 雜技 - 遇文字折行自動縮小字體與靠左對齊

 7 months ago
source link: https://blog.darkthread.net/blog/small-font-when-word-wrap/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

JavaScript 雜技 - 遇文字折行自動縮小字體與靠左對齊-黑暗執行緒

說一下我的需求,有個寬度有限的 HTML 表格欄位,內容文字長度不一,大部分都放得下,但偶爾文字過長會折行使列高加倍,造成排版雜亂且不易閱讀。增加欄位是最簡單的解法,但但因表格欄位眾多難再增大。換個思路,既然無法增加欄寬就讓字小一點,若還是擠不下也別勉強,多行就多行;但另外有個問題,原本文字置中,遇折行時應該要改成靠左比較好。

循著以上思路,我想出「用 JavaScript 檢查 <TD> 內含文字是否折行,若是就將 font-size 設為 smaller。若縮小後還是會折行,就將 text-align 改為 left」的解法,如下圖所示。

Fig3_638417806088486866.png

要如何偵測文字折行呢?問 ChatGPT 跟 Github Copilot 得到用 scrollHeight、offsetHeight 相比的建議,實際上並不可行。最後我在一則 Stackoverflow 討論的冷門回覆找到通關密碼 - getClientRects

getClientRects() 可找出元素在頁面上所佔的矩形區塊大小及位置,當 SPAN 折行時,便會出現兩個以上的矩形區塊,因此我們可以用 spanElement.getClientRects().length > 1 偵測 SPAN 是否折行。如下圖所示,SPAN 「管理員PC」未折行,getClientReacts() 只傳回一個 DOMRect [1]、SPN [辦公區網路印表機]過長折行,getClientReacts() 傳回兩個 DOMRect,分別對映到「辦公區網路印」及「表機」兩個矩形區塊的座標與大小:

Fig2_638417806092625386.png

自動調字形大小的 JavaScript 範例如下,若需要縮小字形,會在該 TD 加上 data-sf Attribute 註記,稍後再檢查一次這些 TD (td[data-sf]),若縮小後內容還是折行便其 text-align 切換成 left:【2024-1-26 更新】換行時靠左對齊可使用 CSS 實現,見文末補充

const isWordWrapped = (elem) => (elem.getClientRects && elem.getClientRects().length > 1);
function AutoSmallerFont() {
    const elems = [...document.querySelector('tbody').querySelectorAll('tr td:first-child')];
    const flagName = 'data-sf';
    // set smaller font size if any word wrapped
    elems.forEach((td) => {
        td.removeAttribute(flagName);
        if ([...td.querySelectorAll('span')].some(isWordWrapped)) {
            td.style.fontSize = 'smaller';
            td.setAttribute(flagName, '');
        }
    });
    // set text-align to left if still word wrapped after font size reduced
    elems.filter(e => e.hasAttribute('data-sf')).forEach(td => {
        if ([...td.querySelectorAll('span')].some(isWordWrapped)) 
            td.style.textAlign = 'left';
    });
}

實測效果如下:線上展示

Fig1_638417806097141049.gif

又學會一個 JavaScript 小把戲。

【2024-01-26 更新】
感謝讀者 Red 分享,元素置中,折行時靠左對齊可使用 Flex 排版 + justify-content: center 實現。程式再改良,靠左改用 CSS 實現可再省下用 JavaScript 處理的工夫。

首先,設定表格第一欄位樣式如下,justify-content: center 可做到元素置中佔滿,內部折行時靠左對齊的效果。

td:first-child {
      display: flex;
      justify-content: center;
}

TD 欄位內的兩個 SPAN 用一個 SPAN 或 DIV 包起來:

<td>
  <span>
    <span class="net">{{entry.net}}</span> /
    <span class="desc">{{entry.desc}}</span>
  </span>
</td>

JavaScript 簡化,偵測折行縮小字體即可:

const isWordWrapped = (elem) => (elem.getClientRects && elem.getClientRects().length > 1);
function AutoSmallerFont() {
    const elems = [...document.querySelector('tbody').querySelectorAll('tr td:first-child')];
    // set smaller font size if any word wrapped
    elems.forEach((td) => {
        if ([...td.querySelectorAll('span > span')].some(isWordWrapped)) {
            td.style.fontSize = 'smaller';
        }
    });
}

線上展示

Fig3_638418739342134528.gif


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK