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.

Platforms
Browser · Node · mobile via Quran Engine
Time
About 10 minutes

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.

Matching on the Arabic string finds nothing, silently

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 gives you one line of the verse

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:

BlockNumbering the element carries
Quran Texteach edition's own — hafs.json is Kufan, warsh.json is Last Madinan
Quran SVGeach muṣḥaf's own medallions — hafs/kfqc 6,236, warsh/kfqc 6,214
Quran SVG Elementsthe split edition's, declared on the root as data-ayah-numbering="kufi"
Quran Enginethe 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 live

Select an ayah on the split page and watch it light up as a set of word groups across several printed lines — never one rectangle.

You wantUse instead
Colour inside a verse by recitation ruleAdd Tajwīd highlighting
Highlight a single word, or follow audio word by wordMake words clickable
A shareable image of one versecrop it — the Quran SVG reference shows the clip