henry_flower: A melancholy wolf (Default)
henry_flower ([personal profile] henry_flower) wrote2021-11-22 08:31 pm

Text Fragments

Контекстне меню свіжих версіях кроуму має пункт 'Copy link to highlight'. Над цією грандіозною функцією гооглівці страждали кілька років та написали специфікацію для неї--Text Fragments.

З гарного для власників сайтів:

  • робити треба нічого.

З поганого для власників сайтів:

  • контроль над функцією є відсутнім;
  • вимкнути її не можна.

Працює вона так: коли користувач виділяє текста і клікає 'Copy link to highlight', до URL додається hash:

http://example.com/hello/#:~:text=world

Якщо бовзера підтримує TF, той шукає рядок world на сторінці та виділяє його. З точки зору devtools жодних змін ув dom не відбувається, автор сторінки вплинути на пошук з виділенням може ніяк.

Кроум 96 має очікуваний бага: якщо URL мав hash виду #/?route_to=heaven (це було популярно використовувати до того як history api з'явився ув Сафарі), кроум його нахабно змінить на #:~:text=..., хоча специфікація дозволяє додавати маркер :~:text=... у кінець.

Власнику сайта хвилюватися про TF є непотрібним: коли він подивиться, наприклад, на location.hash, маркера там не буде--бовзер його заздалегідь видалить самотужки.

Що навело мене на думку: чи можна детектити TF?

Виявляється, через Navigation Timing API можна отримати анмолестед URL зі всіма маркерами:

function has_text_fragment() {
    let url = performance.getEntries().find( v => v.type === 'navigate')?.name || location.href
    return /^#.*:~:text=/.test(new URL(url).hash)
}

Маючи детектор, як можна подратувати too clever by half користувача?

Що може бути більш дратівливим ніж модальні даялоги, які є неможливо закрити? Після намагань запхнути такий модуль ув кілька твітів, я зупинився на

export default (z_index = 999) => has_text_fragment() && draw(z_index)

function has_text_fragment() {
    let url = performance.getEntries().find( v => v.type === 'navigate')?.name || location.href
    return /^#.*:~:text=/.test(new URL(url).hash)
}

function draw(z_index) {
    let d = document, w = d.createElement('div')
    w.style.cssText = `position:fixed;top:0;left:0;height:100%;width:100%;z-index:${z_index};display:flex;justify-content:center;align-items:center;background:rgba(0,0,0,.2)`
    w.innerHTML = '<img src="data:image/gif;base64,R0lGODlhFAAQAOMKAAAAAFp3N3RhMVplU41VL1iNPnyQbKGumMLHuv////b27/b27/b27/b27/b27/b27yH5BAEKAA8ALAAAAAAUABAAAARl8MmJjEUSHTz7O0UYGmJxeFmpiuhDrqrBSSAMc7U6GId1DIPDCRFLHAADTQKBTH5gwEFgZAicHjbbNZctGSSvruoUDgUIaEFgzQ4MniUBek5HX8sFtmAvt3e4NgEzFBo9FocbHhEAOw==">'
    w.querySelector('img').style.cssText = 'image-rendering:pixelated;max-height:100%;max-width:100%;height:500px;width:auto'
    d.body.appendChild(w)
    return w
}

Воно малює модальне жабеня Пепе, коли хтось заходить на сторінку з Text Fragments ув URL:

Має влізти ув 3 твіти (280*3 = 840):

$ terser pepe-the-text-fragment.js --module -mc | wc -c
832