# How to Perform Element-Level Manipulation in OfficeCLI Using DOM Operations

> Learn element-level manipulation in OfficeCLI with DOM operations. This guide explains how OfficeCLI uses SSE for targeted DOM mutations like adding paragraphs or replacing slides.

- Repository: [OfficeAI/OfficeCLI](https://github.com/iofficeai/OfficeCLI)
- Tags: how-to-guide
- Published: 2026-07-28

---

**OfficeCLI uses a two-layer JavaScript architecture that receives Server-Sent Events (SSE) to perform targeted DOM mutations—such as adding Word paragraphs or replacing PowerPoint slides—then re-applies UI decorations via `window._watchReapplyHook()`.**

The **iOfficeAI/OfficeCLI** repository renders Microsoft Office documents as a live browser DOM, enabling real-time document editing through programmatic **element-level manipulation**. By leveraging the patch system in [`watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-sse-core.js) and the decoration layer in [`watch-overlay.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-overlay.js), developers can insert, update, or delete specific document fragments without full page reloads.

## Understanding the Two-Layer Architecture

OfficeCLI’s browser preview relies on two distinct JavaScript layers that separate data mutation from UI decoration.

### Layer 1: Core Rendering and SSE Listener

Located in [[`src/officecli/Resources/watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js)](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js), this layer opens an `EventSource` connection to `/events` and listens for update messages from the server. It parses incoming HTML and performs direct DOM mutations including full-body swaps, block-level patches, and slide add/replace/remove operations.

### Layer 2: Overlay and Decoration

Implemented in [[`src/officecli/Resources/watch-overlay.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-overlay.js)](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-overlay.js), this layer registers `window._watchReapplyHook()` to re-apply selection highlights, text marks, and UI decorations immediately after Layer 1 completes any DOM mutation.

## SSE Update Message Format

The server streams JSON-encoded SSE events containing an `action` field. For **element-level manipulation**, the relevant actions include:

- **`replace`**: Replace an entire slide or document block. Payload includes `slide` (number) and `html` (new markup).
- **`add`**: Insert a new slide or block. Requires `slide` and `html`.
- **`remove`**: Delete a slide or block. Requires `slide` only.
- **`full`**: Replace the entire `<body>` as a fallback when the client has missed messages. Requires `html`.
- **`word-patch` / `excel-patch`**: Apply granular block patches. Payload contains `patches: [{op, block, html?, scrollTo?}]` where `op` can be `add`, `replace`, `remove`, or `style`.

The core message handler resides in **watch-sse-core.js** at lines [17-48](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js#L17-L48), with per-action logic beginning at line [317](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js#L317).

## Performing DOM Mutations

### Full-Body Replacement

When the client receives a `full` action or detects a missed message, Layer 1 executes `_replaceDocumentBody()`:

```javascript
function _replaceDocumentBody(msg) {
    fetch('/').then(r => r.text()).then(html => {
        const doc = new DOMParser().parseFromString(html, 'text/html');
        document.body.innerHTML = doc.body.innerHTML;
        _callReapplyHook();
    });
}

```

This approach creates a fresh document using `DOMParser`, swaps the existing `<body>` content in a single operation to preserve scroll position, then triggers the overlay hook to refresh decorations.

### Block-Level Patches for Word and Excel

For `word-patch` or `excel-patch` actions, the `wordPatchUpdate()` function manipulates content between invisible marker spans (`<span class="wb" data-block="N">` for begin, `<span class="we" data-block="N">` for end):

```javascript
function wordPatchUpdate(msg) {
    msg.patches.forEach(patch => {
        const start = document.querySelector(`.wb[data-block="${patch.block}"]`);
        const end = document.querySelector(`.we[data-block="${patch.block}"]`);
        
        if (patch.op === 'remove') {
            /* remove nodes between start and end */
        } else if (patch.op === 'replace') {
            /* replace inner range */
        } else if (patch.op === 'add') {
            const tmp = document.createElement('div');
            tmp.innerHTML = '<span class="wb" data-block="' + patch.block + '"></span>' 
                          + patch.html 
                          + '<span class="we" data-block="' + patch.block + '"></span>';
            prevEnd.parentNode.insertBefore(tmp.firstChild, prevEnd.nextSibling);
        } else if (patch.op === 'style') {
            /* replace <style> tags */
        }
    });
    _callReapplyHook();
}

```

This implementation (lines [48-90](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js#L48-L90)) allows surgical updates to specific document regions without affecting surrounding content.

### Slide-Specific Actions for PowerPoint

For PowerPoint previews, Layer 1 locates slide containers via `data-slide` attributes:

```javascript
if (msg.action === 'replace') {
    const el = document.querySelector(`.slide-container[data-slide="${msg.slide}"]`);
    if (el) {
        const tmp = document.createElement('div');
        tmp.innerHTML = msg.html;
        el.parentNode.replaceChild(tmp.firstElementChild, el);
        _executeScripts(tmp.firstElementChild);
        _callReapplyHook();
    }
}

```

The `_executeScripts()` function (lines [20-38](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js#L20-L38)) re-creates `<script>` elements to ensure module scripts execute correctly after DOM insertion.

## Re-applying Decorations After Mutations

After any mutation, Layer 1 invokes `window._watchReapplyHook()`. **watch-overlay.js** defines this hook at lines [31-33](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-overlay.js#L31-L33):

```javascript
function reapplyDecorations() {
    applySelectionToDom();
    applyMarks();
}
window._watchReapplyHook = reapplyDecorations;

```

This mechanism keeps selection states and text marks synchronized with the mutated DOM. The mark handling logic resides in lines [300-500](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-overlay.js#L300-500) of **watch-overlay.js**, using pure DOM APIs like `classList.add` and `document.createRange().surroundContents`.

## Client-Side API for Direct Manipulation

OfficeCLI exposes several global methods for programmatic control (defined at lines [34-38](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-overlay.js#L34-38) of **watch-overlay.js**):

- **`window._officecliSetMarks(arr)`**: Replace the current marks array and render them immediately.
- **`window._officecliApplyMarks()`**: Re-apply existing marks after manual DOM changes.
- **`window._officecliReapplyDecorations()`**: Force a full refresh of selections and marks.
- **`window._watchEs`**: The `EventSource` object for custom event listening or debugging.

## Practical Code Examples

### Insert a New Paragraph into Word

```javascript
const patch = {
  op: 'add',
  block: 42,
  html: '<p data-path="/doc/body/para[123]">New paragraph</p>'
};

fetch('/api/patch', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ action: 'word-patch', patches: [patch] })
});

```

When the server broadcasts this as an SSE message, Layer 1 creates the marker spans and inserts the paragraph, then calls `_callReapplyHook()` to refresh the overlay.

### Replace an Existing PowerPoint Slide

```javascript
const slideNum = 5;
const newHtml = `<div class="slide-container" data-slide="${slideNum}">
  <h2>Updated Title</h2>
</div>`;

const el = document.querySelector(`.slide-container[data-slide="${slideNum}"]`);
if (el) {
  const tmp = document.createElement('div');
  tmp.innerHTML = newHtml;
  el.parentNode.replaceChild(tmp.firstElementChild, el);
  _executeScripts(tmp.firstElementChild);
  window._watchReapplyHook();
}

```

### Remove a Block from Word

```javascript
fetch('/api/patch', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    action: 'word-patch',
    patches: [{ op: 'remove', block: 17 }]
  })
});

```

Layer 1 finds the `.wb` and `.we` markers for block 17 and removes all sibling nodes between them (lines [50-57](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js#L50-L57)).

### Apply Temporary CSS Styles

```javascript
fetch('/api/patch', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    action: 'word-patch',
    patches: [{ 
      op: 'style', 
      html: '<style>.officecli-selected{background:#e0f7fa;}</style>' 
    }]
  })
});

```

The `style` operation (lines [40-46](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js#L40-46)) updates the document's `<style>` blocks without touching content markup.

## Summary

- OfficeCLI uses **Server-Sent Events** to drive DOM updates through two layers: core mutation ([`watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-sse-core.js)) and decoration ([`watch-overlay.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-overlay.js)).
- **Block-level patches** rely on invisible marker spans (`.wb`/`.we`) to delimit update regions in Word and Excel documents.
- **Slide-specific actions** target containers via `data-slide` attributes for PowerPoint manipulation.
- After every mutation, `window._watchReapplyHook()` restores selection highlights and text marks.
- Client-side APIs like `window._officecliSetMarks()` provide direct access to the decoration pipeline for custom automation.

## Frequently Asked Questions

### How does OfficeCLI track document blocks for patching?

According to the source code in **WordHandler.cs**, the server generates invisible marker spans with classes `.wb` (word begin) and `.we` (word end) that wrap each content block. These markers carry `data-block` attributes containing numeric identifiers, allowing [`watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-sse-core.js) to precisely locate insertion points and boundaries for `add`, `replace`, and `remove` operations.

### What happens if I miss an SSE update message?

If the client detects a sequence gap or connection interruption, **watch-sse-core.js** falls back to a `full` body replacement. The `_replaceDocumentBody()` function fetches the complete document HTML from the root endpoint, parses it with `DOMParser`, and replaces the entire `<body>` content to ensure synchronization before re-applying decorations.

### Can I manipulate the DOM directly without using SSE patches?

Yes. You can manipulate the DOM directly using standard browser APIs, but you must call `window._officecliReapplyDecorations()` or `window._watchReapplyHook()` afterward to synchronize selection highlights and marks. For programmatic mark management, use `window._officecliSetMarks()` to update the internal state and trigger a re-render.

### How do I preserve PowerPoint slide scripts after DOM replacement?

When replacing slide content, use the `_executeScripts()` helper function from **watch-sse-core.js** (lines [20-38](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js#L20-L38)). This utility re-creates `<script>` elements from the new markup to ensure that module scripts and event handlers execute correctly in the replaced DOM subtree.