September 15, 2026
DOM-Based XSS: A Beginner’s Guide
I want you to checkout my previous blog on XSS before jumping into DOM-Based XSS. Do not miss out the links embedded within words!

By Jessica Shrestha
2 min read
First, let's understand what DOM is!
DOM stands for Document Object Model.
When your browser loads a web page, it doesn't just see the HTML as a bunch of text.
It turns the HTML into a kind of tree structure that JavaScript can understand and interact with.
For example, imagine this HTML:
<h1>Hello, Justin</h1>
<p>Welcome to my website!</p><h1>Hello, Justin</h1>
<p>Welcome to my website!</p>The browser turns it into something like:
HTML
|
BODY
/ \
h1 p
| |
Hello Welcome... HTML
|
BODY
/ \
h1 p
| |
Hello Welcome...This structure is called the DOM.
DOM-based XSS
DOM-Based XSS is an attack where a website's client-side JavaScript takes user-controlled input and inserts it into the webpage in an unsafe way, allowing malicious JavaScript to execute in the victim's browser.
DOM-Based XSS because the attack happens through the DOM (the webpage structure that JavaScript can interact with) in the browser, rather than requiring the malicious input to be stored or processed by the server.
Now, let's see how it can be exploited in controlled environment.
The detail of the code will be explained below.
Step 1: Create an index.html file and paste the code below.
Step 2: Run the HTML file.
Step 3: Test the URL adding #hello in the end or URL.
Step 4: Execute the payload in URL.
<img src=x onerror=alert("XSS")>
Here is what happened when you did those steps.
When we opened the page, the JavaScript looked at the URL:
file:///C:/.../index.html#hellofile:///C:/.../index.html#helloThe important part here is #hello.
The code:
location.hash.substring(1)location.hash.substring(1)takes everything after # and removes the #.
So:
#hello_ becomes: _hello
Then decodeURIComponent() decodes the value if it contains URL-encoded characters.
The value is stored in:
let name = decodeURIComponent(location.hash.substring(1));let name = decodeURIComponent(location.hash.substring(1));So now:
name = "hello"
But where does the XSS happen?
This line is the important part:
document.querySelector("h1").innerHTML = name;document.querySelector("h1").innerHTML = name;innerHTML tells the browser:
"Take this value and treat it as HTML."
That means if name contains normal text like: hello, the browser displays hello.
But if we put HTML containing JavaScript into the URL, the browser doesn't treat it as plain text.
For example:
<img src=x onerror=alert("XSS")><img src=x onerror=alert("XSS")>The browser creates an element.
The image source is x, which doesn't exist, so the error event occurs.
Because the element contains:
onerror=alert("XSS")onerror=alert("XSS")the JavaScript runs and we see the popup.
BEFORE
HTML
|
BODY
/ \
h1 p
| |
Hello Welcome...
|malicious input|
AFTER
HTML
|
BODY
/ \
h1 p
| |
IMG Welcome...
|
onerror
|
alert("XSS")BEFORE
HTML
|
BODY
/ \
h1 p
| |
Hello Welcome...
|malicious input|
AFTER
HTML
|
BODY
/ \
h1 p
| |
IMG Welcome...
|
onerror
|
alert("XSS")