Highlight an ayah
You have a reference — from a search hit, a bookmark, an audio timestamp, a link — and you want the verse to light up. Four blocks can show a verse, and in every one of them the highlight is a lookup by reference, never a search through Arabic text.
Address by reference, not by text
Every block below puts the reference on the node for you, so the highlight is a selector on that identity, not a search through the text.
Two copies of the same ayah from two of our own datasets are routinely not byte-identical: a
shadda and its vowel stored in the other order, a long vowel written as one
code point in one and two in the other. indexOf(text) returns −1 and your highlight never appears.
On text you render: Quran Text
If you followed Display Qur'an text, each ayah is already its own
element carrying data-key. The highlight is one attribute selector:
function highlightAyah(key) { // "2:255"
document.querySelectorAll("#quran [data-key]").forEach((el) => {
el.classList.toggle("is-hit", el.dataset.key === key);
});
}
#quran .is-hit { background: #E3F0EA; border-radius: 4px; }
If you are rendering a whole page rather than a surah, m.page(n).ayahs gives the ayahs on that
page in order, with .key on each, so the elements you create are keyed the same way:
for (const ayah of m.page(42).ayahs) {
const el = document.createElement("span");
el.dataset.key = ayah.key; // "2:253" …
el.textContent = ayah.render({ marks: true, ayahMarks: true }) + " ";
root.append(el);
}
Three things to avoid: wrapping a mark element around a slice of the string at a character offset
(you will cut a letter from its vowel); highlighting by walking the joined
text of a page; and m.search(text), which is for finding words, not for locating a reference you
already hold.
On a printed page: Quran SVG
The page carries one path.ayahPolygon per verse with surah and ayah attributes, shipped at
fill-opacity="0". Raising the opacity is the highlight:
function highlightAyah(root: SVGSVGElement, surah: number, ayah: number) {
root.querySelectorAll<SVGPathElement>(".ayahPolygon").forEach((p) => {
const hit = Number(p.getAttribute("surah")) === surah && Number(p.getAttribute("ayah")) === ayah;
p.setAttribute("fill", "#15705D");
p.setAttribute("fill-opacity", hit ? "0.35" : "0");
});
}
The polygon is the true region of the verse, a stack of line-strips rather than a rectangle, so the
highlight starts and ends mid-line exactly where the verse does. Keep the fill and vary the opacity;
fill: none would also stop the polygon receiving taps. If the verse is not on the mounted page,
find its page first.
On a split page: Quran SVG Elements
On a split page an ayah is a set of word groups, and it is emitted once per printed line it occupies. Select all of the fragments, and colour the ink inside them:
function highlightAyah(svg, key) { // "2:255"
svg.querySelectorAll("g.word path[data-kind]").forEach((p) => p.setAttribute("fill", "#231f20"));
const frags = svg.querySelectorAll(`g.ayah-fragment[data-ayah-key="${key}"]`);
frags.forEach((f) =>
f.querySelectorAll("g.word path[data-kind]").forEach((p) => p.setAttribute("fill", "#15705D")),
);
return frags.length === Number(frags[0]?.dataset.ayahFragments); // true when every line is here
}
querySelector('g.ayah-fragment[data-ayah-key="2:6"]') returns fragment 1 of N. Highlight from it
and you colour the part of the verse on the first line and leave the rest black. It looks correct on
every ayah that fits on one line, which is most of the short ones you will test with; page 1 of the
split Ḥafṣ muṣḥaf is seven such ayahs. Use querySelectorAll, and compare the count with
data-ayah-fragments, which every fragment carries.
There is no background rectangle to tint; colour is a fill on the paths.
In Quran Engine
The engine takes the reference directly and draws a band behind the ink as well as recolouring it. The call returns a handle, and removing the handle undoes exactly that highlight:
const hl = page.highlight("2:255", { mode: "both", ink: "#15705D", band: "#15705D33" });
page.highlightWords(hl); // the word indices it covered
renderer.draw(page, view, window.devicePixelRatio || 1);
page.rehighlight(hl, "2:256"); // the band slides to the next ayah — for following a recitation
highlightBoxes() then returns one shape across every line the ayah occupies, in viewport pixels.
An ayah can continue onto the next page: check page.ayahWordCount(surah, ayah).complete before
you treat one page's highlight as the whole verse.
The reference must be in the muṣḥaf's own numbering
Every recipe above takes surah:ayah. That pair is only meaningful together with the
counting system it came from, and the four blocks do not all use
the same one:
| Block | Numbering the element carries |
|---|---|
| Quran Text | each edition's own — hafs.json is Kufan, warsh.json is Last Madinan |
| Quran SVG | each muṣḥaf's own medallions — hafs/kfqc 6,236, warsh/kfqc 6,214 |
| Quran SVG Elements | the split edition's, declared on the root as data-ayah-numbering="kufi" |
| Quran Engine | the same split edition |
So highlighting "2:255" in the Warsh edition of Quran Text does not highlight āyat al-Kursī; in that edition the verse is numbered 2:253–254. Translate before you highlight:
const target = hafs.ayah(2, 255).to(warsh); // { key: "2:253-254", relation: "split", ayahs: [Ayah, Ayah] }
for (const a of target.ayahs) highlightAyah(a.key);
Map ayah references between counts covers the conversion.
Try it liveSelect an ayah on the split page and watch it light up as a set of word groups across several printed lines — never one rectangle.
Related guides
| You want | Use instead |
|---|---|
| Colour inside a verse by recitation rule | Add Tajwīd highlighting |
| Highlight a single word, or follow audio word by word | Make words clickable |
| A shareable image of one verse | crop it — the Quran SVG reference shows the clip |