---
title: "Remove scripts and event handlers"
url: https://recursiveai.org/onehtml/cookbook/remove-scripts/
description: "Strip script elements, inline event-handler attributes and javascript: links from a page with lossless edits, and know where this stops: onehtml has no…"
version: "1.0.0"
---

# Remove scripts and event handlers

Strip script elements, inline event-handler attributes and javascript: links from a page with lossless edits, and know where this stops: onehtml has no HTML sanitizer.

```csharp
using System.Text;
using RecursiveAI.OneHtml;
using RecursiveAI.OneHtml.Editing;

byte[] input = File.ReadAllBytes("events.html");
Document doc = Document.Parse(input);

var edits = new List<Edit>();
foreach (Node script in doc.Root.QuerySelectorAll("script"))
{
    edits.Add(Edit.Remove(script));
}
foreach (Node element in doc.Root.DescendantElements)
{
    foreach (Attr attr in element.Attributes)
    {
        if (attr.LocalName.StartsWith("on", StringComparison.OrdinalIgnoreCase))
        {
            edits.Add(Edit.RemoveAttribute(element, attr.QualifiedName));
        }
    }
}
foreach (Node link in doc.Root.QuerySelectorAll("a[href^='javascript:' i]"))
{
    edits.Add(Edit.RemoveAttribute(link, "href"));
}

EditResult result = doc.Apply(edits);
Console.WriteLine($"{edits.Count} edits, succeeded: {result.Succeeded}");
foreach (EditedRange range in result.Changes!.Ranges)
{
    string removed = Encoding.UTF8.GetString(input, range.OldByteRange.Start, range.OldByteRange.Length);
    Console.WriteLine($"- {removed.Trim()}");
}
File.WriteAllBytes("events.noscript.html", result.Document!.ToLosslessBytes());
```

```text
3 edits, succeeded: True
- <script src="/js/analytics.js"></script>
- <script>
    document.querySelectorAll('.card').forEach(c => c.addEventListener('click', track));
  </script>
- onclick="window.scrollTo(0, 0)"
```

The edits run as one transaction. Each is aimed at a node of the same snapshot, and either all apply or none do. Everything else in the file keeps its bytes. A removed element leaves the indentation of its line behind, as this diff shows:

```text
12c12
<   <script src="/js/analytics.js"></script>
---
>   
52,55c52,53
<   <script>
<     document.querySelectorAll('.card').forEach(c => c.addEventListener('click', track));
<   </script>
<   <button onclick="window.scrollTo(0, 0)" class="to-top">Back to top</button>
---
>   
>   <button class="to-top">Back to top</button>
```

**This is not a sanitizer.** onehtml has no sanitizing API and no allow-list of safe markup. The code above removes exactly what it names: `script` elements, attributes starting with `on`, and `href` values starting with `javascript:`. HTML can run or load code in many other ways that this doesn’t touch, such as `iframe`, `object`, `embed`, SVG, `meta http-equiv="refresh"`, `base href`, form actions and CSS. Use it to clean up pages you control, such as before an archive or a diff. Don’t use it to make untrusted HTML safe to show to users. For that, use a dedicated allow-list sanitizer.

The parser itself is safe on hostile input: scripts are inert text, nothing is fetched, and input limits are checked before the work they guard. That protects your process, not the people who later open the HTML.

To remove other elements the same way, change the selector, for example `Edit.Remove` for every `iframe` or `noscript`. The edit operations are in [Editing](https://recursiveai.org/onehtml/docs/editing/).
