Menu
CoddyTech

Advanced HTML Interview Questions

Script loading, the rendering path, parser error recovery, resource hints, responsive images, web components and security.

12 questions
  1. What is the difference between async and defer on a script tag?

    Both download the script without blocking parsing. async runs it as soon as it arrives, in any order; defer runs scripts after parsing, in document order, before DOMContentLoaded.

    <script src="analytics.js" async></script>
    <script src="vendor.js" defer></script>
    <script src="app.js" defer></script>  <!-- runs after vendor.js -->
    

    Use defer for app code and async for independent scripts like analytics. Module scripts are deferred by default, and both attributes are ignored on inline classic scripts.

  2. How does the browser turn HTML into pixels (the critical rendering path)?

    HTML becomes the DOM and CSS the CSSOM; they combine into the render tree of visible nodes, then the browser runs layout, paint and compositing.

    A classic script without async or defer pauses parsing, and stylesheets block rendering. To speed up the first render, defer scripts, inline the critical CSS and preload the hero image.

  3. What is the difference between DOMContentLoaded and load?

    DOMContentLoaded fires when the HTML is parsed and deferred scripts have run; load fires later, after images, stylesheets and iframes have loaded too.

    HTML
    <!DOCTYPE html>
    <ol id="log"></ol>
    <script>
      const log = (msg) => {
        const li = document.createElement('li');
        li.textContent = msg + ' at ' + Math.round(performance.now()) + ' ms';
        document.getElementById('log').append(li);
      };
      log('inline script');
      document.addEventListener('DOMContentLoaded', () => log('DOMContentLoaded'));
      window.addEventListener('load', () => log('load'));
    </script>

    Start DOM code on DOMContentLoaded (or with defer); waiting for load delays the app until every image arrives.

  4. What does the browser do with invalid HTML like a <div> inside a <p>?

    It never shows an error; the parser follows defined recovery rules. A <div> start tag closes an open <p>, so the div ends up after the paragraph.

    HTML
    <!DOCTYPE html>
    <div id="box"><p>One<div>Two</div></p><table><tr><td>Cell</td></tr></table></div>
    <p>What the parser built:</p>
    <pre id="out" style="white-space: pre-wrap;"></pre>
    <script>
      document.getElementById('out').textContent = document.getElementById('box').innerHTML;
    </script>

    The result is <p>One</p><div>Two</div><p></p>: the stray </p> makes an empty paragraph, and the table gets a <tbody> you never wrote, so table > tr matches nothing. The parser keeps a stack of open elements to decide what closes what.

    Practice it: Valid Parentheses

  5. Can an <a> element contain a <div>?

    Yes, since HTML5. <a> has a transparent content model: it may contain whatever its parent may contain, minus interactive content (another link, a button, an input). So a link wrapping a card is valid inside a <div> but not inside a <p>, which accepts only phrasing content.

    HTML
    <!DOCTYPE html>
    <style>
      .card { display: block; max-width: 260px; padding: 8px 12px; border: 1px solid #cbd5e1; border-radius: 8px; color: inherit; text-decoration: none; }
      .card:hover { border-color: #3b82f6; }
    </style>
    <a class="card" href="#course">
      <div><strong>HTML course</strong></div>
      <div>40 lessons, free to start</div>
    </a>
    <p id="course">The whole card is one link.</p>
  6. What is the difference between preload, prefetch and preconnect?

    preload fetches a resource the current page needs soon, at high priority. prefetch fetches what the next page will probably need, at low priority. preconnect opens the connection (DNS, TCP, TLS) to another origin without fetching anything.

    <link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
    <link rel="preload" href="/hero.avif" as="image">
    <link rel="prefetch" href="/checkout.js">
    <link rel="preconnect" href="https://cdn.example.com">
    <link rel="dns-prefetch" href="https://analytics.example.com">
    

    preload needs as to get the right priority, and fonts need crossorigin even on the same origin or they download twice.

  7. How do you lazy load images, and which image should not be lazy loaded?

    Add loading="lazy" to <img> or <iframe> and the browser waits until it nears the viewport. Never lazy load the largest image visible on first load (usually the hero), because that delays the Largest Contentful Paint.

    <img src="hero.avif" alt="..." width="1200" height="600" fetchpriority="high">
    <img src="card.avif" alt="..." width="400" height="300" loading="lazy" decoding="async">
    

    Set width and height on every image so the browser reserves the space and nothing shifts (CLS).

  8. How do srcset, sizes and <picture> work?

    srcset lists sizes of one image and sizes says how wide it will display, so the browser downloads the smallest file that is sharp enough. <picture> chooses between different images or formats.

    <img src="photo-800.jpg"
         srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w"
         sizes="(max-width: 600px) 100vw, 50vw"
         alt="Team at work" width="800" height="533">
    
    <picture>
      <source srcset="photo.avif" type="image/avif">
      <source srcset="photo.webp" type="image/webp">
      <img src="photo.jpg" alt="Team at work" width="800" height="533">
    </picture>
    

    w descriptors give each file's width; x descriptors (1x, 2x) suit fixed-size images. The <img> inside <picture> is required: it renders and carries alt.

  9. What are Web Components and the Shadow DOM?

    Web Components are browser-native reusable elements: custom elements (your own tags, defined with a class), the shadow DOM (a private tree whose styles do not leak in or out) and templates with slots.

    HTML
    <!DOCTYPE html>
    <style>p { color: crimson; }</style>
    <p>Outside: styled by the page.</p>
    <user-badge name="Asha"></user-badge>
    <script>
      class UserBadge extends HTMLElement {
        constructor() {
          super();
          this.attachShadow({ mode: 'open' });
        }
        connectedCallback() {
          const p = document.createElement('p');
          p.textContent = 'Hello, ' + this.getAttribute('name');
          const style = document.createElement('style');
          style.textContent = 'p { color: teal; font-weight: bold; }';
          this.shadowRoot.replaceChildren(style, p);
        }
      }
      customElements.define('user-badge', UserBadge);
    </script>

    The page's p { color: crimson } does not reach the paragraph inside the component. Custom element names need a hyphen, and inherited properties like color and CSS custom properties do pass into the shadow tree.

  10. How do you embed third-party content safely with an iframe?

    Add sandbox and grant only the permissions the frame needs. An empty sandbox blocks scripts, forms, popups and top-level navigation, and gives the frame a unique origin.

    <iframe src="https://widget.example.com"
            sandbox="allow-scripts allow-forms"
            allow="fullscreen"
            referrerpolicy="no-referrer"
            loading="lazy"
            title="Weather widget"></iframe>
    

    Never combine allow-scripts with allow-same-origin for content from your own origin: the framed script could remove its own sandbox. To stop other sites framing yours, send Content-Security-Policy: frame-ancestors 'self'. See the iframe page.

  11. Why is setting innerHTML with user input dangerous?

    innerHTML parses the string as HTML, so user input becomes markup. Inserted <script> tags do not run, but event handler attributes do: <img src=x onerror="steal()"> runs the attacker's code. That is cross-site scripting (XSS).

    HTML
    <!DOCTYPE html>
    <input id="name" value="<b>Asha</b> <i>Rao</i>" size="30">
    <p>textContent: <span id="safe"></span></p>
    <p>innerHTML: <span id="unsafe"></span></p>
    <script>
      const input = document.getElementById('name');
      const show = () => {
        document.getElementById('safe').textContent = input.value;
        document.getElementById('unsafe').innerHTML = input.value;
      };
      input.addEventListener('input', show);
      show();
    </script>

    Use textContent for text, and sanitize any user HTML with a library such as DOMPurify.

  12. What is progressive enhancement?

    Progressive enhancement means building the core of the page in plain HTML that works everywhere, then adding CSS and JavaScript on top, so the page still works when a layer fails or loads slowly.

    <form action="/search" method="get">
      <input name="q" type="search" aria-label="Search">
      <button>Search</button>
    </form>
    <!-- JavaScript can intercept this form for instant results;
         without JavaScript it still submits and works. -->
    

    Graceful degradation goes the other way: it starts from the full experience and adds fallbacks.

Coddy programming languages illustration

Learn to code with Coddy

Get started