← Back to jQuery Course | Chapter 15: Performance & Best Practices | Lesson 5 of 5

Security and XSS Prevention

Cross-site scripting, or XSS, can happen when untrusted text is inserted as HTML. Treat user input as data unless HTML is intentionally required.
Syntax
javascript
$(selector).text(userInput); // safe
$(selector).html(userInput); // unsafe with untrusted input

What is XSS?

Cross-site scripting (XSS) happens when untrusted text — something typed by a user, or returned from an external API — is inserted into the page as executable HTML instead of being treated as plain text. If that text contains a script tag or an event handler attribute, the browser will run it, potentially letting an attacker execute code in your users' sessions.

Example: What is XSS?

javascript
<!DOCTYPE html>
<html>
<head>
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
  <style>
    button { padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border:none; border-radius:4px; cursor:pointer; }
    #resetBtn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
    #output { margin-top:10px; padding:8px; border:1px solid #ccc; border-radius:4px; background:#f7f7f7; font-family:monospace; min-height:24px; }
    #demo :where(p, div, span, li, h1, h2, h3, h4, td, th, blockquote, label, section, article, aside) { border:1px solid #4a7fb5; background:#eaf2fb; padding:6px 10px; margin:4px 0; border-radius:4px; }
    #demo :where(ul, ol) { border:1px dashed #888; background:#fafafa; padding:8px 8px 8px 32px; margin:6px 0; }
    #demo :where(table) { border-collapse:collapse; }
    #demo :where(td, th) { border-radius:0; }
    #demo :where(input, select, textarea) { margin:4px 0; }
  </style>
</head>
<body>
  <button id="runBtn">Run</button>
  <button id="resetBtn">Reset</button>
  <section id="demo">
    <div id="output"></div>

  </section>
  <script>
    var original = $("#demo").html();

    $("#runBtn").click(function() {
      const userInput = "<img src=x onerror=alert(1)>";
      $("#output").text(userInput);
    });

    $("#resetBtn").click(function() {
      $("#demo").html(original);
      $("#output").empty();
    });
  </script>
</body>
</html>

text() vs html()

The text() method treats whatever value you give it as plain text and escapes it safely, while html() parses the string as actual HTML markup. Because of this, html() should never be used directly with untrusted input — doing so is one of the most common ways XSS vulnerabilities end up in real applications.

Example: text() vs html()

javascript
<!DOCTYPE html>
<html>
<head>
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
  <style>
    button { padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border:none; border-radius:4px; cursor:pointer; }
    #resetBtn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
    #output { margin-top:10px; padding:8px; border:1px solid #ccc; border-radius:4px; background:#f7f7f7; font-family:monospace; min-height:24px; }
    .btn { display:inline-block; padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border-radius:4px; cursor:pointer; font:13px sans-serif; }
    #resetBtn.btn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
    #demo :where(p, div, span, li, h1, h2, h3, h4, td, th, blockquote, label, section, article, aside) { border:1px solid #4a7fb5; background:#eaf2fb; padding:6px 10px; margin:4px 0; border-radius:4px; }
    #demo :where(ul, ol) { border:1px dashed #888; background:#fafafa; padding:8px 8px 8px 32px; margin:6px 0; }
    #demo :where(table) { border-collapse:collapse; }
    #demo :where(td, th) { border-radius:0; }
    #demo :where(input, select, textarea) { margin:4px 0; }
  </style>
</head>
<body>
  <span id="runBtn" class="btn" role="button">Run</span>
  <span id="resetBtn" class="btn" role="button">Reset</span>
  <section id="demo">
    <p id="safe"></p><p id="risky"></p>

  </section>
  <div id="output"></div>
  <script>
    var original = $("#demo").html();

    $("#runBtn").click(function() {
      const input = "<b>bold</b>";
      $("#safe").text(input);
      $("#risky").html(input);
    });

    $("#resetBtn").click(function() {
      $("#demo").html(original);
      $("#output").empty();
    });
  </script>
</body>
</html>

Escape Untrusted Content

Whenever content comes from users or from an external system you don't fully control, keep it as plain text rather than inserting it as HTML. This prevents the browser from ever treating that content as executable markup, closing off the XSS risk entirely for that piece of data.

Example: Escape Untrusted Content

javascript
<!DOCTYPE html>
<html>
<head>
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
  <style>
    button { padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border:none; border-radius:4px; cursor:pointer; }
    #resetBtn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
    #output { margin-top:10px; padding:8px; border:1px solid #ccc; border-radius:4px; background:#f7f7f7; font-family:monospace; min-height:24px; }
    #demo :where(p, div, span, li, h1, h2, h3, h4, td, th, blockquote, label, section, article, aside) { border:1px solid #4a7fb5; background:#eaf2fb; padding:6px 10px; margin:4px 0; border-radius:4px; }
    #demo :where(ul, ol) { border:1px dashed #888; background:#fafafa; padding:8px 8px 8px 32px; margin:6px 0; }
    #demo :where(table) { border-collapse:collapse; }
    #demo :where(td, th) { border-radius:0; }
    #demo :where(input, select, textarea) { margin:4px 0; }
  </style>
</head>
<body>
  <button id="runBtn">Run</button>
  <button id="resetBtn">Reset</button>
  <section id="demo">
    <div id="comment"></div>

  </section>
  <div id="output"></div>
  <script>
    var original = $("#demo").html();

    $("#runBtn").click(function() {
      const userComment = "Nice post!";
      $("#comment").text(userComment);
    });

    $("#resetBtn").click(function() {
      $("#demo").html(original);
      $("#output").empty();
    });
  </script>
</body>
</html>

Avoid Unsafe String Building

Building HTML by concatenating strings that include untrusted values is a common way XSS risks sneak into an application, since it's easy to forget to escape something. Creating elements programmatically and setting their text content with text() instead avoids this risk altogether.

Example: Avoid Unsafe String Building

javascript
<!DOCTYPE html>
<html>
<head>
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
  <style>
    button { padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border:none; border-radius:4px; cursor:pointer; }
    #resetBtn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
    #output { margin-top:10px; padding:8px; border:1px solid #ccc; border-radius:4px; background:#f7f7f7; font-family:monospace; min-height:24px; }
    #demo :where(p, div, span, li, h1, h2, h3, h4, td, th, blockquote, label, section, article, aside) { border:1px solid #4a7fb5; background:#eaf2fb; padding:6px 10px; margin:4px 0; border-radius:4px; }
    #demo :where(ul, ol) { border:1px dashed #888; background:#fafafa; padding:8px 8px 8px 32px; margin:6px 0; }
    #demo :where(table) { border-collapse:collapse; }
    #demo :where(td, th) { border-radius:0; }
    #demo :where(input, select, textarea) { margin:4px 0; }
  </style>
</head>
<body>
  <button id="runBtn">Run</button>
  <button id="resetBtn">Reset</button>
  <section id="demo">
    <div id="output"></div>

  </section>
  <script>
    var original = $("#demo").html();

    $("#runBtn").click(function() {
      const username = "Alice";
      $("<p>").text("Hello, " + username).appendTo("#output");
    });

    $("#resetBtn").click(function() {
      $("#demo").html(original);
      $("#output").empty();
    });
  </script>
</body>
</html>

Small Security Project

A simple, reliable rule to follow: treat any value that came from a user or an external source as untrusted by default. Use text() when displaying it, and validate or sanitize data before it's ever sent somewhere it could be executed.

Example: Small Security Project

javascript
<!DOCTYPE html>
<html>
<head>
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
  <style>
    button { padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border:none; border-radius:4px; cursor:pointer; }
    #resetBtn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
    #output { margin-top:10px; padding:8px; border:1px solid #ccc; border-radius:4px; background:#f7f7f7; font-family:monospace; min-height:24px; }
    #demo :where(p, div, span, li, h1, h2, h3, h4, td, th, blockquote, label, section, article, aside) { border:1px solid #4a7fb5; background:#eaf2fb; padding:6px 10px; margin:4px 0; border-radius:4px; }
    #demo :where(ul, ol) { border:1px dashed #888; background:#fafafa; padding:8px 8px 8px 32px; margin:6px 0; }
    #demo :where(table) { border-collapse:collapse; }
    #demo :where(td, th) { border-radius:0; }
    #demo :where(input, select, textarea) { margin:4px 0; }
  </style>
</head>
<body>
  <button id="runBtn">Run</button>
  <button id="resetBtn">Reset</button>
  <section id="demo">
    <ul id="feed"></ul>

  </section>
  <div id="output"></div>
  <script>
    var original = $("#demo").html();

    $("#runBtn").click(function() {
      $("#output").empty();
      const comments = ["Great!", "<img src=x onerror=alert(1)>"];
      comments.forEach(function(c) {
        $("<li>").text(c).appendTo("#feed");
      });
    });

    $("#resetBtn").click(function() {
      $("#demo").html(original);
      $("#output").empty();
    });
  </script>
</body>
</html>
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Using .html(userInput) for text a user typed, which can run injected markup like <img onerror=...>; use .text(userInput).
  2. Trying to sanitize by removing <script> tags only, when attackers can use other tags and attributes such as onerror.
  3. Building HTML by concatenating strings such as "<p>" + name + "</p>" with untrusted input, when the value should be set with .text() afterwards.
Chapter Summary
  • Caching selectors and using chaining avoid repeated work.
  • Efficient event handling and reducing reflow and repaint keep pages responsive.
  • Security practices, including XSS prevention, keep jQuery code safe.
🔒

Chapter Quiz — Complete all 5 topics to unlock

0/5 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.