擾人的 lazy loading

維基百科上面有時候會想要看其他語言的頁面,以 Kalafina 這頁來說,想要點開跨國語系的 menu 點日文版頁面的時候,會出現這樣的情況:

游標移上去要點的時候,因為 lazy loading 跑完;DOM 被插入新的 node,造成本來想要的日文版連結跑到下面,然後按快一點的人就點到其他連結... 目前的解法是直接用 uBlock Origin 擋掉那個 node,讓他不要出現:

wikipedia.org##.cx-uls-relevant-languages-banner

這種類型的 UX flaw 在現代愈來愈多了,而且還是各 framework 都推薦的寫法... (各種 useEffect() 類的功能,頁面的結構先出來,再後續讀到內容時再更新頁面)

看到「Creating Perfect Font Fallbacks in CSS」這邊這段時想到的:

With font-display: swap, the browser will first render your text using the fallback typeface you specified in the font-family property[.]

font swapping 也是個讓人吐血的 UX flaw...

Popover API

看到「Popover API (developer.mozilla.org)」這個討論,引用的資料是 MDN 上面的「Popover API」,從名字也可以看出來與 pop over 有關 (話說查單字發現 popover 這個詞在字典裡居然是泡芙,英文維基百科上的 Popover 也可以看到...)。

Anyway,馬上有想到的是 modal 類的操作,在 MDN 上面的文件裡面有範例,可以用純 HTML 的方式操作:

<button popovertarget="mypopover">Toggle the popover</button>
<div id="mypopover" popover>Popover content</div>

效果就是在畫面正中央出現,預設有 border。

另外也可以透過 JavaScript 的方式操作:

HTMLElement.togglePopover()

MDN 文件把這個功能標成 Baseline 2024:

Since April 2024, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

因為是很新的東西 (就支援度來說),要注意如果使用者是舊版瀏覽器的 fallback 行為。

翻文件底部的支援情況,可以看到最後支援的主流瀏覽器是 Firefox 125 (2024/04/16),而 Firefox 還是有人會用 ESR 版本,目前還是 115,但照著 Firefox ESR 的節奏,應該是暑假會出下一版,但如果是現在要用的話,應該得考慮用 polyfill 去支援不原生支援的瀏覽器。

HTML 上的注音標示

Fediverse 上面看到 itszero 提到 HTML 的注音標示:

翻了「HTML Ruby Markup Extensions」這邊的資料,從 GitHub 上面看起來是三月的時候加進去的 commit:「
Import new Draft」,不過目前測了 stable 版的 Brave (Chromium-based) 與 Firefox 都還沒支援。

我把測試丟在 https://jsfiddle.net/5sedmoLu/ 這邊,後續有新消息的時候可以直接看看效果...

利用信件裡面的 CSS,讓文字只在轉寄後才出現

在「Kobold letters: HTML emails are a risk (lutrasecurity.com)」這邊看到的 security advisory (算... 是吧?),原文在「Kobold Letters」這邊,如同標題寫的,方法其實意外的簡單...

Thunderbird 是透過 .moz-text-html>div> 指定就可以達到效果:

Outlook on the web (i.e. 雲端版本) 則是有在 id 上面增加隨機的 prefix 避免,但可以用 body>div> 避開,另外有些眉眉角角的地方會稍微複雜一點,但還是可行的:

Gmail 則是直接用個簡單的 css selector 掛上 display: none; 就 OK 了:在 sender 端 (轉寄者) 看不到,在 receiver 端則可以 (效果更好?):

比較慘的是目前大家都沒有想到比較好的解法,就算這次提到的方法被補上了,應該還是很容易被繞過去:

Unfortunately, for the foreseeable future, it is sadly not realistic to expect email clients to implement robust mitigation. This means that it is up to the users to be aware of the dangers of HTML emails and to take the necessary precautions.

另外文章裡面提到了 Can I email 這個網站,看起來如果要自己處理 email 內容的話是個不錯的資源...

Keyboard Event:在網頁上自定義快速鍵

先前寫了一堆 userscript 針對不同的網站加上快速鍵 (像是翻下一頁或是上一頁):

然後發現太多重複的事情了,就寫成 Chrome 的 extension 了:「Keyboard Event」,程式碼在 GitHub 上有放:「gslin/keyboard-event」,不信任一致性的話也可以用 Chrome extension source viewer 直接看 chrome web store 上面放的版本。

這個 extension 的想法是設定 hostname、path 以及按鍵,接著設定按鍵按下後會觸發的 DOM element (用 CSS selector 選) 與 DOM event (像是 mouseover 這樣的 event 字串)。

而其中 click event 常常不會動 (對應實際的呼叫是 element.dispatchEvent(new Event('click'))),在 debug 時發現需要用 click() (對應實際的呼叫是 element.click()),所以有特別接受 click() 這個字串。

這個舉個例子,我有一組設定是:

www.instagram.com
.*
<
div[role="dialog"] button[aria-label="Go back"]
click()

www.instagram.com
.*
>
div[role="dialog"] button[aria-label="Next"]
click()

在瀏覽器上面看 Instagram 的照片時可以用 <> 快速切換照片。

另外設定的部分支援 chrome.storage 的 browser sync,對於有多瀏覽器時會自動同步設定檔,不用每一台都設定一次...

這算是第一次寫 Manifest V3 的 extension (先前都是 V2),本來想用 Vue.js 處理設定頁 options.html,但遇到了 CSP 的問題,在 Manifest V3 上面無法設定 unsafe-eval,於是 Vue.js 只能用 runtime 版本,喪失了 template 好用的地方...

後來決定回頭用 jQuery + jQuery UI (用 sortable),然後自己處理一堆 data 更新時頁面的變化,一整個 2010 年老人味都出來了...

但這樣至少會動,先跑一陣子看看...

CSS Flexbox 裡元素的 margin 不會重疊

前陣子在動手弄 css 的東西,才注意到 flex 有很多不太一樣的設計,對於學過「以前的標準」的人來說會比較意外,就像標題提到的...

我在 JS Fiddle 上面做了一個範例可以測試,裡面有兩組 container,第一組就很標準的 div 設定為 100x100 的大小,然後 margin 設為 10px

可以看到第一組裡面,上下 block 的 margin 會「疊」起來 (也就是上面 block 的 margin-bottom 與下面 block 的 margin-top 疊起來),變成 10px,而不是分開算的 20px

這個特性可以用「margin collapsing」查到,在 MDN 上甚至有一篇「Mastering margin collapsing」可以看,而這個特性對於老人來說已經用習慣了...

但如果用 flex 實作時 (display: flex; 以及 flex-direction: column;),會發現所有的 margin 都是自己計算而不會疊加,這點在 W3C 的文件「CSS Flexible Box Layout Module Level 1」裡面有提到:

For example, floats do not intrude into the flex container, and the flex container’s margins do not collapse with the margins of its contents.

這個特性無法被改變,取而代之的是用 gap 這個 property 設定元素之間的間格。

arXiv 提供 HTML 版本介面 (beta 版)

Hacker News 上看到「ArXiv now offers papers in HTML format (arxiv.org)」這則,arXiv 推出了 beta 版的 HTML 介面:「Accessibility update: arXiv now offers papers in HTML format」。

不是每一篇都有上,需要是最近用 TeX 類格式上傳的才會轉:

We are happy to announce that as of Monday, December 18th, arXiv is now generating an HTML formatted version of all papers submitted in TeX/LaTeX (as long as papers were submitted on or after December 1st, 2023 and HTML conversion is successful – more on this below).

所以我先找了二十年前 Poincaré conjecture (龐加萊猜想) 的三篇論文,就沒有 HTML 版本:「The entropy formula for the Ricci flow and its geometric applications」、「Ricci flow with surgery on three-manifolds」、「Finite extinction time for the solutions to the Ricci flow on certain three-manifolds」。

Hacker News 的 comment 裡面有人給了有 HTML 版本的論文:「The detectability of single spinless stellar-mass black holes through gravitational lensing of gravitational waves with advanced LIGO」,以 render 的效果看起來還不錯?

另外這個站目前看起來沒有在 Fastly 上:

;; ANSWER SECTION:
browse.arxiv.org.       300     IN      A       34.160.61.147

應該等成熟進 GA 時會把所有 TeX 檔案都轉出來?

漢字字形的處理

Hacker News 上看到「Your Code Displays Japanese Wrong (heistak.github.io)」這篇,原文是「Your Code Displays Japanese Wrong」。

這個算是 CJK 族群的經典問題,主要的問題是有不同的團體都在使用漢字,但雖然都是 U+5203 的「刃」,在不同的地區的「標準寫法」是不一樣的,像是「國家教育研究院 - 教育部國語小字典-刃」這邊就有 SVG 版本的「圖」可以看:

我在 different-lang.html 這邊把他提到的「刃直海角骨入」給放了進去,指定不同的 lang,像是這樣:

<dt>lang="jp"</dt><dd lang="jp">刃直海角骨入</dd>

如果你的電腦裡面有 Noto Sans CJK 的話,應該可以看出不同的字形。

在 HTML 網頁上可以利用 html 內的 lang 資訊告訴瀏覽器去抓取對應的字形,當然,系統有沒有這個字形又是另外一回事了,不過市場上至少有 open source license 的 Noto CJK 系列,算是個低標的答案可以用。

至於要更多樣的話,應該是要分不同語言下去找...

修好 Trac 1.6 上的 TracSubtickets

Trac 1.6 總算從死了三年的 Python 2.7 換成了 Python 3,所以算是蠻強大的升級動力,但也可以想像到相關的 plugin 其實因此爛了不少,加上 Trac 現再用的人愈來愈少,沒有人會修這些問題,所以你就得當「沒有人」跳下去修...

標題上提到的 TracSubtickets 算是這樣的一個套件,他的概念很好用,但大概從 Trac 1.2 以後就沒什麼更新了,先前有遇到 MySQL 8.0 的資料庫搭配起來會撞到關鍵字而出錯,得自己修。

而這次遇到的問題是 TracSubtickets 在頁面輸出子票資訊時用到 ITemplateStreamFilter 這個功能,而從官方文件開頭也可以看到問題:Trac 1.4 的時候內部的 template engine 就從自家研發的 Genshi 換成了 Python 社群用的更廣泛的 Jinja2,但當時只是先標成 deprecated,還沒到不能用,直到 Trac 1.5.1 時拔掉了,所以接下來的 Trac 1.6 就沒得用了。

在官方的「Replacing the ITemplateStreamFilter interface」有提出建議的方法,是用 JavaScript 改 DOM:

The only way left to alter the generated content is to perform these modifications dynamically on client-side using JavaScript.

我看了半天 Trac 1.6 的程式碼,看起來的確沒有什麼比較好的方法可以處理... 只能回來照官方的方法走,後續的問題就是看要處理的多乾淨 (或是多髒)。

因為 Trac 本身沒有 client template engine (像是 React 或是 Vue 之類的),我決定這邊還是讓 server 端全部把 html string 都產生出來,再由 client 端生一個 div 直接用 innerHTML 塞進去:這樣就不用傳一包 JSON 到 client 端慢慢組了...

於是就出現了這包 diff/patch:「Comparing cae40fb..master · gslin/trac-subtickets-plugin」。

基本的思路是,既然以前的 filter_stream() 是產生 html tree 的程式碼,那就重複拿來用,把結果輸出轉成 html string,用 add_script_data 丟到 window 下的 global variable (喔耶),再寫一段 javascript 把這串東西塞進本來在的 DOM 位置。

這樣至少就能動了...