באמצעות 'קטעי טקסט' אפשר לציין קטע טקסט בקטע של כתובת ה-URL. כשמנווטים לכתובת URL עם קטע טקסט כזה, הדפדפן יכול להדגיש אותו ו/או להביא אותו לידיעת המשתמש.
מזהים של קטעים
Chrome 80 הייתה גרסה חשובה. היא הכילה כמה תכונות שציפינו להן מאוד, כמו מודולים של ECMAScript ב-Web Workers, שילוב של nullish, שרשרת אופציונלית ועוד. כדרכן, ההודעה על ההשקה פורסמה בפוסט בבלוג של Chromium. בצילום המסך שבהמשך אפשר לראות קטע מפוסט הבלוג.
בטח אתם תוהים מה המשמעות של כל התיבות האדומות. הן התוצאה של הפעלת קטע הקוד הבא ב-DevTools. הוא מדגיש את כל הרכיבים שיש להם מאפיין id
.
document.querySelectorAll('[id]').forEach((el) => {
el.style.border = 'solid 2px red';
});
אני יכול להוסיף קישור עומק לכל רכיב שמודגש בקופסה אדומה, באמצעות מזהה הקטע, שאז משמש אותי בגיבוב של כתובת ה-URL של הדף. נניח שרציתי ליצור קישור עומק לתיבה שליחת משוב בפורומים של המוצרים שבצד, הייתי יכול ליצור את כתובת ה-URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1
באופן ידני.
כפי שאפשר לראות בחלונית הרכיבים של הכלים למפתחים, לרכיב המדובר יש מאפיין id
עם הערך HTML1
.
אם נתבונן בכתובת ה-URL הזו באמצעות ה-constructor URL()
של JavaScript, נראה את הרכיבים השונים.
שימו לב למאפיין hash
עם הערך #HTML1
.
new URL('https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1');
/* Creates a new `URL` object
URL {
hash: "#HTML1"
host: "blog.chromium.org"
hostname: "blog.chromium.org"
href: "https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1"
origin: "https://blog.chromium.org"
password: ""
pathname: "/2019/12/chrome-80-content-indexing-es-modules.html"
port: ""
protocol: "https:"
search: ""
searchParams: URLSearchParams {}
username: ""
}
*/
עם זאת, העובדה שנאלצתי לפתוח את הכלים למפתחים כדי למצוא את id
של רכיב מסוים ממחישה את הסבירות שהקטע הספציפי הזה בדף היה אמור להיות מקושר על ידי מחבר הפוסט בבלוג.
מה קורה אם רוצים לקשר למשהו בלי id
? נניח שאני רוצה לקשר לכותרת מודולים של ECMAScript ב-Web Workers. כפי שאפשר לראות בצילום המסך שבהמשך, ל-<h1>
המדובר אין מאפיין id
, כלומר אין לי אפשרות לקשר לכותרת הזו. זו הבעיה שאפשר לפתור באמצעות 'קטעי טקסט'.
קטעי טקסט
ההצעה Text Fragments מוסיפה תמיכה באפשרות לציין קטע טקסט בגיבוב של כתובת ה-URL. כשמנווטים לכתובת URL עם קטע טקסט כזה, סוכן המשתמש יכול להדגיש אותו ו/או להביא אותו לידיעת המשתמש.
תאימות דפדפן
מטעמי אבטחה, כדי שהתכונה הזו תפעל, צריך לפתוח את הקישורים בהקשר של noopener
.
לכן, חשוב לכלול את הערך rel="noopener"
בסימון ה-<a>
של העוגן, או להוסיף את הערך noopener
לרשימת Window.open()
של תכונות הפונקציונליות של החלון.
start
בצורתו הפשוטה ביותר, התחביר של קטעי טקסט הוא: סמל ה-hash #
ואחריו :~:text=
ולבסוף start
, שמייצג את הטקסט שרוצים לקשר אליו בקוד של אחוזים.
#:~:text=start
לדוגמה, נניח שאני רוצה לקשר לכותרת ECMAScript Modules in Web Workers בפוסט בבלוג שבו פורסמו תכונות ב-Chrome 80. במקרה כזה, כתובת ה-URL תהיה:
קטע הטקסט מודגש ככה. אם לוחצים על הקישור בדפדפן תומך כמו Chrome, קטע הטקסט מודגש ומגלול למיקום שבו הוא גלוי:
start
וגם end
מה קורה אם רוצים לקשר לקטע כולו שנקרא מודולים של ECMAScript ב-Web Workers, ולא רק לכותרת שלו? קידוד האחוזים של כל הטקסט בקטע יהפוך את כתובת ה-URL שנוצרת לארוכה מדי לשימוש.
למרבה המזל, יש דרך טובה יותר. במקום הטקסט כולו, אפשר להגדיר את הטקסט הרצוי באמצעות התחביר start,end
. לכן, מציינים כמה מילים עם קידוד אחוזים בתחילת הטקסט הרצוי וכמה מילים עם קידוד אחוזים בסוף הטקסט הרצוי, מופרדים בפסיקים ,
.
כך זה נראה:
עבור start
, יש לי ECMAScript%20Modules%20in%20Web%20Workers
, ואז פסיק ,
ואחריו ES%20Modules%20in%20Web%20Workers.
בתור end
. כשלוחצים עליו בדפדפן נתמך כמו Chrome, הקטע כולו מודגש והגלילה מתבצעת כך שהוא יוצג במסך:
עכשיו יכול להיות שתתהו לגבי הבחירה שלי ב-start
וב-end
. למעשה, גם כתובת ה-URL הקצרה יותר https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript%20Modules,Web%20Workers.
עם שתי מילים בלבד בכל צד הייתה עובדת. משווים את הערכים של start
ו-end
לערכי הערכים הקודמים.
אם אמשיך צעד אחד קדימה ואשתמש עכשיו רק במילה אחת גם עבור start
וגם עבור end
, אפשר לראות שאני בבעיה. כתובת ה-URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript,Workers.
קצרה יותר עכשיו, אבל קטע הטקסט המודגש הוא כבר לא הקטע הרצוי המקורי. ההדגשה נעצרת במופע הראשון של המילה Workers.
, וזה נכון, אבל זה לא מה שרציתי להדגיש. הבעיה היא שהערכים הנוכחיים של start
ו-end
, שהם מילה אחת, לא מזהים באופן ייחודי את הקטע הרצוי:
prefix-
וגם -suffix
שימוש בערכים ארוכים מספיק עבור start
ו-end
הוא פתרון אחד לקבלת קישור ייחודי.
עם זאת, יש מצבים שבהם אי אפשר לעשות זאת. דרך אגב, למה בחרתי בפוסט בבלוג על השקת Chrome 80 כדוגמה? התשובה היא שבגרסה הזו הושקנו 'קטעי טקסט':
שימו לב שבצילום המסך שלמעלה המילה 'טקסט' מופיעה ארבע פעמים. ההופעה הרביעית כתובה בגופן קוד ירוק. אם רציתי לקשר למילה הספציפית הזו, הייתי מגדיר את start
לערך text
. מכיוון שהמילה 'טקסט' היא מילה אחת בלבד, לא יכול להיות end
. מה עכשיו? כתובת ה-URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=text
מתאימה למופע הראשון של המילה 'טקסט' שכבר מופיעה בכותרת:
למרבה המזל, יש פתרון. במקרים כאלה, אפשר לציין prefix-
ו--suffix
. המילה לפני הגופן הירוק של הקוד 'text' היא 'the', והמילה שאחריה היא 'parameter'. בשום אחת מהפעמים האחרות שבהן מופיעה המילה 'text' אין את אותן מילים מסביב. בעזרת המידע הזה, אפשר לשנות את כתובת ה-URL הקודמת ולהוסיף את prefix-
ואת -suffix
. כמו שאר הפרמטרים, גם הם צריכים להיות בקידוד אחוזים ויכולים להכיל יותר ממילה אחת.
https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=the-,text,-parameter
.
כדי לאפשר למנתח לזהות בבירור את prefix-
ואת -suffix
, צריך להפריד אותם מ-start
ומ-end
האופציונלי באמצעות מקף -
.
התחביר המלא
התחביר המלא של קטעי טקסט מוצג בהמשך. (סוגריים מרובעים מציינים פרמטר אופציונלי).
הערכים של כל הפרמטרים צריכים להיות מקודדים לפי אחוזים. חשוב במיוחד להקפיד על כך לגבי התווים מקף -
, סימן & &
ופסיק ,
, כדי שהם לא יפורשו כחלק מהסינטקס של הוראה הטקסט.
#:~:text=[prefix-,]start[,end][,-suffix]
כל אחד מהפרמטרים prefix-
, start
, end
ו--suffix
יתאים רק לטקסט ברכיב יחיד ברמת הבלוק, אבל טווחים מלאים של start,end
יכולים לחול על כמה בלוקים. לדוגמה, הביטוי :~:text=The quick,lazy dog
לא יתאים בדוגמה הבאה כי מחרוזת ההתחלה 'The quick' לא מופיעה ברכיב יחיד ללא הפסקה ברמת הבלוק:
<div>
The
<div></div>
quick brown fox
</div>
<div>jumped over the lazy dog</div>
עם זאת, הוא תואם בדוגמה הזו:
<div>The quick brown fox</div>
<div>jumped over the lazy dog</div>
יצירת כתובות URL של קטעי טקסט באמצעות תוסף לדפדפן
יצירת כתובות URL של קטעי טקסט באופן ידני היא משימה מייגעת, במיוחד כשצריך לוודא שהן ייחודיות. אם אתם רוצים, תוכלו למצוא במפרט כמה טיפים ושלבים מדויקים ליצירת כתובות URL של קטעי טקסט. אנחנו מספקים תוסף דפדפן בקוד פתוח שנקרא קישור לקטעי טקסט, שמאפשר ליצור קישור לכל טקסט על ידי בחירה בו ואז לחיצה על 'העתקת הקישור לטקסט שנבחר' בתפריט ההקשר. התוסף הזה זמין בדפדפנים הבאים:
- קישור לקטע טקסט ב-Google Chrome
- קישור לקטע טקסט ב-Microsoft Edge
- קישור לקטע טקסט ב-Mozilla Firefox
- קישור לקטע טקסט ב-Apple Safari
כמה קטעי טקסט בכתובת URL אחת
חשוב לזכור שיכולים להופיע כמה קטעי טקסט בכתובת URL אחת. יש להפריד בין קטעי הטקסט הספציפיים באמצעות סימן האמפרסנד &
. לפניכם קישור לדוגמה עם שלושה קטעי טקסט:
https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=Text%20URL%20Fragments&text=text,-parameter&text=:~:text=On%20islands,%20birds%20can%20contribute%20as%20much%20as%2060%25%20of%20a%20cat's%20diet
.
שילוב בין רכיבים לבין קטעי טקסט
אפשר לשלב קטעי רכיבים רגילים עם קטעי טקסט. אפשר להשתמש בשניהם באותה כתובת URL, למשל כדי לספק חלופה משמעותית במקרה שהטקסט המקורי בדף ישתנה, כך שקטע הטקסט כבר לא יתאים. כתובת ה-URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1:~:text=Give%20us%20feedback%20in%20our%20Product%20Forums.
שמקשרת לקטע שליחת משוב בפורומים של המוצרים מכילה גם שבר של רכיב (HTML1
) וגם שבר טקסט (text=Give%20us%20feedback%20in%20our%20Product%20Forums.
):
ההוראה של מקטע הקוד
יש רכיב אחד בתחביר שעדיין לא הסברתי: הוראה של קטע קוד :~:
. כדי למנוע בעיות תאימות עם קטעי רכיבים קיימים של כתובות URL, כפי שמוצג למעלה, במפרט של קטעי טקסט מופיעה ההנחיה לגבי קטעים. הוראה על קטע קוד היא חלק מקטע כתובת ה-URL שמוגדר על ידי רצף הקוד :~:
. הוא שמור להוראות של סוכן המשתמש, כמו text=
, והוא מוחסר מכתובת ה-URL במהלך הטעינה כדי שסקריפטים של מחברים לא יוכלו לקיים איתו אינטראקציה ישירה. הוראות לסוכן משתמש נקראות גם הוראות. במקרה הזה, text=
נקרא הנחיית טקסט.
זיהוי תכונות
כדי לבדוק אם יש תמיכה, בודקים את הנכס fragmentDirective
לקריאה בלבד ב-document
. ההנחיה לגבי קטע הקוד היא מנגנון שמאפשר לכתובות URL לציין הוראות שמופנות לדפדפן ולא למסמך. המטרה היא למנוע אינטראקציה ישירה עם סקריפט של המחבר, כדי שאפשר יהיה להוסיף בעתיד הוראות לסוכני משתמשים בלי חשש להכניס שינויים קיצוניים בתוכן הקיים. דוגמה אפשרית להוספות כאלה בעתיד יכולה להיות טיפים לתרגום.
if ('fragmentDirective' in document) {
// Text Fragments is supported.
}
זיהוי התכונות מיועד בעיקר למקרים שבהם קישורים נוצרים באופן דינמי (למשל, על ידי מנועי חיפוש), כדי להימנע מהצגת קישורים של קטעי טקסט בדפדפנים שלא תומכים בהם.
עיצוב קטעי טקסט
כברירת מחדל, הדפדפנים מעניקים קטעי טקסט את אותו סגנון שהם מעניקים ל-mark
(בדרך כלל שחור על צהוב, צבעי המערכת של CSS עבור mark
). גיליון הסגנונות של סוכן המשתמש מכיל CSS שנראה כך:
:root::target-text {
color: MarkText;
background: Mark;
}
כפי שאפשר לראות, הדפדפן חושף סלקטור מדומה ::target-text
שאפשר להשתמש בו כדי להתאים אישית את ההדגשה שחלה. לדוגמה, אפשר לעצב את קטעי הטקסט כך שיהיו בצבע שחור על רקע אדום. כמו תמיד, חשוב לבדוק את ניגודיות הצבעים כדי שהעיצוב החלופי לא יגרום לבעיות נגישות, ולוודא שההדגשה בולטת מבחינה ויזואלית משאר התוכן.
:root::target-text {
color: black;
background-color: red;
}
יכולת להוסיף קוד למילוי חסר (polyfill)
אפשר לבצע מילוי של תכונות של קטעי טקסט (Text Fragments) עד גבול מסוים. אנחנו מספקים polyfill, שההרחבה משתמשת בו באופן פנימי, לדפדפנים שלא מספקים תמיכה מובנית בקטעי טקסט שבהם הפונקציונליות מיושמת ב-JavaScript.
יצירת קישורים של קטעי טקסט באופן פרוגרמטי
ה-polyfill מכיל את הקובץ fragment-generation-utils.js
שאפשר לייבא ולהשתמש בו כדי ליצור קישורים של קטעי טקסט. הקוד לדוגמה מפורט בהמשך:
const { generateFragment } = await import('https://unpkg.com/text-fragments-polyfill/dist/fragment-generation-utils.js');
const result = generateFragment(window.getSelection());
if (result.status === 0) {
let url = `${location.origin}${location.pathname}${location.search}`;
const fragment = result.fragment;
const prefix = fragment.prefix ?
`${encodeURIComponent(fragment.prefix)}-,` :
'';
const suffix = fragment.suffix ?
`,-${encodeURIComponent(fragment.suffix)}` :
'';
const start = encodeURIComponent(fragment.textStart);
const end = fragment.textEnd ?
`,${encodeURIComponent(fragment.textEnd)}` :
'';
url += `#:~:text=${prefix}${start}${end}${suffix}`;
console.log(url);
}
אחזור קטעי טקסט למטרות ניתוח נתונים
הרבה אתרים משתמשים בקטעים לצורך ניתוב, ולכן הדפדפנים מסירים את קטעי הטקסט כדי לא לשבור את הדפים האלה. יש צורך מובהק לחשוף קישורים של קטעי טקסט לדפים, למשל למטרות ניתוח נתונים, אבל הפתרון המוצע עדיין לא יושם. בינתיים, אפשר להשתמש בקוד הבא כדי לחלץ את המידע הרצוי.
new URL(performance.getEntries().find(({ type }) => type === 'navigate').name).hash;
אבטחה
ההנחיות לקטע טקסט מופעלות רק בניווט מלא (לא באותו דף) כתוצאה מהפעלה על ידי משתמש.
בנוסף, ניווטים שמקורם במקור שונה מהיעד ידרשו שהניווט יתבצע בהקשר של noopener
, כך שידוע שדף היעד מבודד מספיק. הנחיות לקטע טקסט חלות רק על המסגרת הראשית. כלומר, לא יתבצע חיפוש טקסט בתוך מסגרות iframe, וניווט ב-iframe לא יפעיל קטע טקסט.
פרטיות
חשוב שהטמעות של מפרט קטעי הטקסט לא יגרמו לדליפת מידע לגבי העובדה שקטעי טקסט נמצאו בדף או לא. קטעי אלמנטים נמצאים בשליטה מלאה של מחבר הדף המקורי, אבל כל אחד יכול ליצור קטעי טקסט. זכור לכם שבדוגמה שלמעלה לא הייתה אפשרות לקשר לכותרת ECMAScript Modules in Web Workers, כי ל-<h1>
לא היה id
, אבל כל אחד, כולל אני, יכול פשוט לקשר לכל מקום על ידי עיצוב מדויק של קטע הטקסט?
נניח שאני מנהל רשת מודעות זדונית בשם evil-ads.example.com
. נניח שבאחד מהפריטים מסוג iframe במודעה יצרתי באופן דינמי פריט iframe מוסתר שמגיע ממקורות שונים (CORS) אל dating.example.com
עם כתובת URL של קטע טקסט dating.example.com#:~:text=Log%20Out
, אחרי שהמשתמש יוצר אינטראקציה עם המודעה. אם הטקסט 'יציאה מהחשבון' מופיע, סימן שהקורבן מחובר כרגע ל-dating.example.com
, ואוכל להשתמש בכך כדי ליצור פרופיל משתמש. מכיוון שהטמעה פשוטה של Text Fragments עשויה להחליט שתאמה מוצלחת צריכה לגרום להעברת המיקוד, ב-evil-ads.example.com
אפשר להאזין לאירוע blur
וכך לדעת מתי התרחשה התאמה. הטמענו ב-Chrome קטעי טקסט כך שלא ניתן יהיה ליצור את התרחיש שמתואר למעלה.
התקפה אחרת יכולה להיות ניצול של תעבורת הנתונים ברשת על סמך מיקום הגלילה. נניח שיש לי גישה ליומני תעבורת הנתונים של הרשת של הקורבן, למשל כאדמין של אינטראנט פנימי של חברה. עכשיו נניח שיש מסמך ארוך של משאבי אנוש בשם מה עושים אם סובלים מ…, ואחריו רשימה של מצבים כמו שחיקה, חרדה וכו'. אוכל להציב פיקסל מעקב לצד כל פריט ברשימה. אם לאחר מכן אבחן שהטעינה של המסמך מתרחשת בו-זמנית עם טעינת פיקסל המעקב לצד פריט מסוים, למשל burn out, אוכל לקבוע, כאדמין של האינטרנט, שעובד לחץ על קישור לקטע טקסט עם :~:text=burn%20out
, ויכול להיות שהעובד הניח שהמידע סודי ולא גלוי לאף אחד. מכיוון שהדוגמה הזו היא מלאכותית במידה מסוימת מלכתחילה, ומכיוון לניצול שלה נדרשות מאוד תנאים מוקדמים ספציפיים, צוות האבטחה של Chrome העריך שהסיכון בהטמעת גלילה במהלך ניווט הוא בר-ניהול.
סוכנויות משתמשים אחרות עשויות להחליט להציג במקום זאת רכיב UI של גלילה ידנית.
אתרים שרוצים לבטל את ההסכמה יכולים לשלוח את הערך של הכותרת Document Policy ב-Chromium, כדי שסוכנויות המשתמשים לא יעבדו כתובות URL של קטעי טקסט.
Document-Policy: force-load-at-top
השבתת קטעי טקסט
הדרך הפשוטה ביותר להשבית את התכונה היא להשתמש בתוסף שיכול להחדיר כותרות תגובה של HTTP. לדוגמה, ModHeader (לא מוצר של Google) כדי להוסיף כותרת תגובה (לא בקשה) באופן הבא:
Document-Policy: force-load-at-top
אפשרות נוספת, מורכבת יותר, היא לבטל את ההסכמה באמצעות ההגדרה הארגונית ScrollToTextFragmentEnabled
.
כדי לעשות זאת ב-macOS, מדביקים את הפקודה הבאה במסוף.
defaults write com.google.Chrome ScrollToTextFragmentEnabled -bool false
ב-Windows, פועלים לפי ההוראות במסמכים באתר התמיכה של Google Chrome Enterprise.
קטעי טקסט בחיפוש באינטרנט
בחיפושים מסוימים, מנוע החיפוש Google מספק תשובה מהירה או סיכום שכולל קטע טקסט של תוכן מהאתר הרלוונטי. לרוב, קטעי המידע בתוצאות החיפוש הראשונות האלה מופיעים כשהחיפוש מנוסח כשאלה. לחיצה על תקציר תוצאת החיפוש הראשונה מפנה את המשתמש ישירות אל קטע הטקסט מתקציר תוצאת החיפוש הראשונה שבדף האינטרנט המקורי. התכונה הזו פועלת באמצעות כתובות URL של קטעי טקסט שנוצרו באופן אוטומטי.
סיכום
כתובת URL של מקטעי טקסט היא תכונה חזקה שמאפשרת לקשר לטקסט שרירותי בדפי אינטרנט. הקהילה האקדמית יכולה להשתמש בו כדי לספק קישורים מדויקים מאוד למקורות או להפניות. מנועי חיפוש יכולים להשתמש בו כדי ליצור קישור עומק לתוצאות טקסט בדפים. אתרי רשתות חברתיות יכולים להשתמש בה כדי לאפשר למשתמשים לשתף קטעים ספציפיים מדף אינטרנט במקום צילומי מסך שאינם נגישים. אני מקווה שתתחילו להשתמש בכתובות URL של קטעי טקסט ותמצאו אותן מועילות כמוני. חשוב להתקין את התוסף לדפדפן קישור לקטעי טקסט.
קישורים רלוונטיים
- טיוטת מפרט
- בדיקת תגים
- הרשומה 'סטטוס הפלטפורמה של Chrome'
- באג מעקב ב-Chrome
- שרשור לגבי כוונה לשלוח את ההזמנה
- שרשור ב-WebKit-Dev
- שרשור בנושא עמדת Mozilla בנושא תקנים
תודות
ניק בוריס ודוד בוקן הטמיעו את התכונה 'קטעי טקסט' ופירטו אותה, עם תרומות מגראנט וואנג. תודה ל-Joe Medley על הבדיקה המעמיקה של המאמר הזה. התמונה הראשית (Hero) היא של Greg Rakozy מ-Unsplash.