String.prototype.indexOf()

Returns the index of the first occurrence of a substring, or -1 when it is not found.

Signature
indexOf(searchString)
indexOf(searchString, position)
  • Added in JavaScript ECMAScript 1

Parameters

ParameterTypeDescription
$searchStringstringThe substring to search for. All values are coerced to strings, so omitting it searches for the literal text "undefined".
$positionoptionalnumber= 0The index at or after which the match must start. Values below 0 behave like 0; values above str.length mean the string is not searched at all.

Return value

numberThe index of the first occurrence of searchString at or after position, or -1 if it is not found.

indexOf() performs a case-sensitive, left-to-right scan and reports where the match starts. Use it when you need the position; if you only need a yes/no answer, includes() says so more clearly.

The -1 trap

A match at the very start returns 0, which is falsy. if (s.indexOf(x)) and if (s.indexOf(x) > 0) are both wrong — the only correct tests are !== -1 and >= 0. This is the single most common bug on this method.

Empty search strings

Searching for "" returns position clamped to the string length: "hello world".indexOf("") is 0, indexOf("", 3) is 3, and indexOf("", 13) is 11. It never returns -1, so an unfiltered empty term always looks like a hit.

Finding every occurrence

Loop with the second argument, restarting from found + 1 (or found + needle.length if overlapping matches should be skipped). Forgetting to advance the offset gives you an infinite loop that finds the same index forever.

Matching is by UTF-16 code unit, so the returned index is a code-unit offset — feed it straight into slice(), not into a per-character count.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Split a header at its first colon without a regex
  • Find where a marker starts so you can slice around it
  • Collect every position of a repeated token in a document
  • Check for a substring in engines too old for includes()

Frequently asked questions

How do I find the position of a substring in JavaScript?

Call str.indexOf(substring); it returns the zero-based index of the first match, or -1 if there is none. Pass a second argument to start searching further along.

Why does indexOf() return 0 instead of true?

It returns a position, not a boolean, and 0 means the match starts at the first character. Compare with !== -1, or use includes() when you only want a boolean.

How do I find all occurrences of a substring?

Loop, passing the previous index plus one as the position argument, until indexOf() returns -1. Advance by the needle's length instead if overlapping matches should be ignored.

More examples

Every occurrence of a substring

Restarting one past each hit walks the whole string. The second line shows overlapping matches: starting at 1 and 2 still finds "aa", which a non-overlapping scan would skip.

Example · javascript
const hay = "the rain in spain";
const positions = [];
let i = hay.indexOf("in");
while (i !== -1) {
  positions.push(i);
  i = hay.indexOf("in", i + 1); // must advance, or this never ends
}
console.log(positions); // [ 6, 9, 15 ]

console.log("aaaa".split("").map((_, n) => "aaaa".indexOf("aa", n)));
// [ 0, 1, 2, -1 ]

Testing for presence correctly

Index 0 is a real match but a falsy number, so only the explicit comparisons (or includes()) report it correctly.

Example · javascript
const role = "admin:write";

console.log(role.indexOf("admin")); // 0
console.log(Boolean(role.indexOf("admin"))); // false — the bug
console.log(role.indexOf("admin") !== -1); // true
console.log(role.indexOf("admin") >= 0); // true
console.log(role.includes("admin")); // true — no comparison needed
console.log(role.indexOf("admin", 1)); // -1 — position excludes it

Discussion

  • Be the first to comment on this lesson.