Skip to main content

DHTMLX Spreadsheet. Readonly via events example

This demo shows how to implement a custom readonly mode in DHTMLX Spreadsheet by intercepting before* events to selectively block user actions while keeping the component interactive.

Live example

const spreadsheet = new dhx.Spreadsheet("spreadsheet");

spreadsheet.events.on("beforeEditStart", function () {
  return false;
});
spreadsheet.events.on("beforeValueChange", function () {
  return false;
});
spreadsheet.events.on("beforeStyleChange", function () {
  return false;
});
spreadsheet.events.on("beforecolumndelete", function () {
  return false;
});
spreadsheet.events.on("beforerowdelete", function () {
  return false;
});
spreadsheet.events.on("beforecolumnadd", function () {
  return false;
});
spreadsheet.events.on("beforerowadd", function () {
  return false;
});

spreadsheet.parse(dataset);
<!-- component container -->
<div style="height: 100%; max-width:100%" id="spreadsheet"></div>

<!-- dataset -->

<script>
const dataset = {
        styles: {
            bold: {
                "font-weight": "bold",
            },
            right: {
                "justify-content": "flex-end",
                "text-align": "right",
            },
        },
        data: [
            { cell: "a1", value: "Country", css:"bold" },
            { cell: "b1", value: "Product", css:"bold" },
            { cell: "c1", value: "Price", css:"right bold" },
            { cell: "d1", value: "Amount", css:"right bold" },
            { cell: "e1", value: "Total Price", css:"right bold" },

            { cell: "a2", value: "Ecuador" },
            { cell: "b2", value: "Banana" },
            { cell: "c2", value: 6.68, format: "currency" },
            { cell: "d2", value: 430 },
            { cell: "e2", value: 2872.4, format: "currency" },

            { cell: "a3", value: "Belarus" },
            { cell: "b3", value: "Apple" },
            { cell: "c3", value: 3.75, format: "currency" },
            { cell: "d3", value: 600 },
            { cell: "e3", value: 2250, format: "currency" },

            { cell: "a4", value: "Peru" },
            { cell: "b4", value: "Grapes" },
            { cell: "c4", value: 7.69, format: "currency" },
            { cell: "d4", value: 740 },
            { cell: "e4", value: 5690.6, format: "currency" },

            { cell: "a5", value: "Egypt" },
            { cell: "b5", value: "Orange" },
            { cell: "c5", value: 5.86, format: "currency" },
            { cell: "d5", value: 560 },
            { cell: "e5", value: 3281.6, format: "currency" },

            { cell: "a6", value: "South Africa" },
            { cell: "b6", value: "Grapefruit" },
            { cell: "c6", value: 8.58, format: "currency" },
            { cell: "d6", value: 800 },
            { cell: "e6", value: 6864, format: "currency" },

            { cell: "a7", value: "Spain" },
            { cell: "b7", value: "Lemon" },
            { cell: "c7", value: 9.12, format: "currency" },
            { cell: "d7", value: 650 },
            { cell: "e7", value: 5928, format: "currency" },

            { cell: "a8", value: "Iran" },
            { cell: "b8", value: "Pomegranate" },
            { cell: "c8", value: 9.67, format: "currency" },
            { cell: "d8", value: 300 },
            { cell: "e8", value: 2901, format: "currency" }
        ],
    };
</script>

Applications that display shared reports or template spreadsheets often need to prevent structural modifications (adding or deleting rows and columns) while still allowing users to navigate, select cells, and copy data. The built-in readonly config blocks all interaction, but a custom approach using events provides fine-grained control over exactly which operations are permitted.

This example subscribes to several before* events (beforeEditStart, beforeValueChange, beforeStyleChange, beforeColumnAdd, beforeRowAdd, beforeColumnDelete, and beforeRowDelete), returning false from each handler to cancel the operation. Users can still scroll, select cells, and interact with the toolbar, but any attempt to modify cell content or the grid structure is silently prevented.

Solution overview

  1. Create the Spreadsheet with new dhx.Spreadsheet("spreadsheet", config)
  2. Attach handlers via spreadsheet.events.on("beforeEditStart", () => false) for each action to block
  3. Repeat for beforeValueChange, beforeStyleChange, beforeColumnAdd, beforeRowAdd, beforeColumnDelete, beforeRowDelete
  4. Load data with spreadsheet.parse(data) after event handlers are set

Key points

  • Granular control: Unlike the readonly: true config which disables all interaction, event-based blocking lets you choose exactly which actions to prevent
  • Handler order: Register event handlers before users start interacting with the Spreadsheet so edit and structure changes are blocked from the beginning
  • Conditional logic: Each handler can include conditions. For example, allow edits in specific columns or for certain user roles by returning true conditionally

API reference

Additional resources