123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- <!doctype html>
- <html>
- <head>
- <meta charset='utf-8'>
- <title>Custom HTML in cells and headers - Handsontable</title>
- <!--
- Loading Handsontable (full distribution that includes all dependencies)
- -->
- <link data-jsfiddle="common" rel="stylesheet" media="screen" href="../dist/handsontable.css">
- <link data-jsfiddle="common" rel="stylesheet" media="screen" href="../dist/pikaday/pikaday.css">
- <script data-jsfiddle="common" src="../dist/pikaday/pikaday.js"></script>
- <script data-jsfiddle="common" src="../dist/moment/moment.js"></script>
- <script data-jsfiddle="common" src="../dist/zeroclipboard/ZeroClipboard.js"></script>
- <script data-jsfiddle="common" src="../dist/numbro/numbro.js"></script>
- <script data-jsfiddle="common" src="../dist/numbro/languages.js"></script>
- <script data-jsfiddle="common" src="../dist/handsontable.js"></script>
- <!--
- Loading demo dependencies. They are used here only to enhance the examples on this page
- -->
- <link data-jsfiddle="common" rel="stylesheet" media="screen" href="css/samples.css?20140331">
- <script src="js/samples.js"></script>
- <script src="js/highlight/highlight.pack.js"></script>
- <script src="js/moment/moment.js"></script>
- <script src="js/pikaday/pikaday.js"></script>
- <link rel="stylesheet" media="screen" href="js/highlight/styles/github.css">
- <link rel="stylesheet" href="css/font-awesome/css/font-awesome.min.css">
- <link rel="stylesheet" href="js/pikaday/css/pikaday.css">
- <!--
- Facebook open graph. Don't copy this to your project :)
- -->
- <meta property="og:title" content="Custom HTML in cells and headers">
- <meta property="og:description"
- content="This example shows how to use custom cell renderers to display HTML content in a cell and header.">
- <meta property="og:url" content="http://handsontable.com/demo/reference_html.html">
- <meta property="og:image" content="http://handsontable.com/demo/image/og-image.png">
- <meta property="og:image:type" content="image/png">
- <meta property="og:image:width" content="409">
- <meta property="og:image:height" content="164">
- <link rel="canonical" href="http://handsontable.com/demo/reference_html.html">
- <!--
- Google Analytics for GitHub Page. Don't copy this to your project :)
- -->
- <script src="js/ga.js"></script>
- </head>
- <body>
- <div class="wrapper">
- <div class="wrapper-row">
- <div id="global-menu-clone">
- <h1><a href="../index.html">Handsontable</a></h1>
- </div>
- <div id="container">
- <div class="columnLayout">
- <div class="rowLayout">
- <div class="descLayout">
- <div class="pad">
- <h2>Custom HTML in cells and headers</h2>
- <p>On this page:</p>
- <ul>
- <li><a href="#cell">Rendering custom HTML in cells</a></li>
- <li><a href="#header">Rendering custom HTML in header</a></li>
- <li><a href="#dropdown">Changing cell type from a dropdown menu in cell header</a></li>
- </ul>
- </div>
- </div>
- </div>
- <div class="rowLayout">
- <div class="descLayout">
- <div class="pad" data-jsfiddle="example1">
- <a name="cell"></a>
- <h2>Rendering custom HTML in cells</h2>
- <p>This example shows how to use custom cell renderers to display HTML content in a cell.</p>
- <p>This is a very powerful feature. Just remember to escape any HTML code that could be used for XSS
- attacks.</p>
- <p>In the below configuration:</p>
- <ul>
- <li><strong>Title</strong> column uses built-in HTML renderer that allows any HTML. This is unsafe if your code comes from untrusted source. Take notice that a Handsontable user can use it to enter <code><script></code> or other potentially malicious tags using the cell editor!</li>
- <li><strong>Description</strong> column also uses HTML renderer (same as above)</li>
- <li><strong>Comments</strong> column uses a custom renderer (<code>safeHtmlRenderer</code>). This should be safe for user input, because only certain tags are allowed</li>
- <li><strong>Cover</strong> column accepts image URL as a string and converts it to a <code><img></code> in the renderer</li>
- </ul>
- <div id="example1"></div>
- <p>
- <button name="dump" data-dump="#example1" data-instance="hot1" title="Prints current data source to Firebug/Chrome Dev Tools">
- Dump data to console
- </button>
- </p>
- </div>
- </div>
- <div class="codeLayout">
- <div class="pad">
- <div class="jsFiddle">
- <button class="jsFiddleLink" data-runfiddle="example1">Edit in jsFiddle</button>
- </div>
- <script data-jsfiddle="example1">
- var data = [
- {
- title: "<a href='http://www.amazon.com/Professional-JavaScript-Developers-Nicholas-Zakas/dp/1118026691'>Professional JavaScript for Web Developers</a>",
- description: "This <a href='http://bit.ly/sM1bDf'>book</a> provides a developer-level introduction along with more advanced and useful features of <b>JavaScript</b>.",
- comments: "I would rate it ★★★★☆",
- cover: "http://ecx.images-amazon.com/images/I/51bRhyVTVGL._SL50_.jpg"
- },
- {
- title: "<a href='http://shop.oreilly.com/product/9780596517748.do'>JavaScript: The Good Parts</a>",
- description: "This book provides a developer-level introduction along with <b>more advanced</b> and useful features of JavaScript.",
- comments: "This is <big>the</big> book about JavaScript",
- cover: "http://ecx.images-amazon.com/images/I/51gdVAEfPUL._SL50_.jpg"
- },
- {
- title: "<a href='http://shop.oreilly.com/product/9780596805531.do'>JavaScript: The Definitive Guide</a>",
- description: "<em>JavaScript: The Definitive Guide</em> provides a thorough description of the core <b>JavaScript</b> language and both the legacy and standard DOMs implemented in web browsers.",
- comments: "I've never actually read it, but the <a href='http://shop.oreilly.com/product/9780596805531.do'>comments</a> are highly <strong>positive</strong>.",
- cover: "http://ecx.images-amazon.com/images/I/51VFNL4T7kL._SL50_.jpg"
- }
- ],
- container1,
- hot1;
- container1 = document.getElementById('example1');
- hot1 = new Handsontable(container1, {
- data: data,
- colWidths: [200, 200, 200, 60],
- colHeaders: ["Title", "Description", "Comments", "Cover"],
- columns: [
- {data: "title", renderer: "html"},
- {data: "description", renderer: "html"},
- {data: "comments", renderer: safeHtmlRenderer},
- {data: "cover", renderer: coverRenderer}
- ]
- });
- // original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
- function strip_tags(input, allowed) {
- var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
- commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
- // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
- allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join('');
- return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
- return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
- });
- }
- function safeHtmlRenderer(instance, td, row, col, prop, value, cellProperties) {
- var escaped = Handsontable.helper.stringify(value);
- escaped = strip_tags(escaped, '<em><b><strong><a><big>'); //be sure you only allow certain HTML tags to avoid XSS threats (you should also remove unwanted HTML attributes)
- td.innerHTML = escaped;
- return td;
- }
- function coverRenderer (instance, td, row, col, prop, value, cellProperties) {
- var escaped = Handsontable.helper.stringify(value),
- img;
- if (escaped.indexOf('http') === 0) {
- img = document.createElement('IMG');
- img.src = value;
- Handsontable.dom.addEvent(img, 'mousedown', function (e){
- e.preventDefault(); // prevent selection quirk
- });
- Handsontable.dom.empty(td);
- td.appendChild(img);
- }
- else {
- // render as text
- Handsontable.renderers.TextRenderer.apply(this, arguments);
- }
- return td;
- }
- </script>
- </div>
- </div>
- </div>
- <div class="rowLayout">
- <div class="descLayout">
- <div class="pad" data-jsfiddle="example2">
- <a name="header"></a>
- <h2>Rendering custom HTML in header</h2>
- <p>You can also put HTML into row and column headers.</p>
- <p>If you need to attach events to DOM elements like the checkbox below, just remember to identify the element
- by class name, not by id. This is because row and column headers are duplicated in the DOM tree and id
- attribute must be unique.</p>
- <div id="example2"></div>
- <p>
- <button name="dump" data-dump="#example2" data-instance="hot2" title="Prints current data source to Firebug/Chrome Dev Tools">
- Dump data to console
- </button>
- </p>
- </div>
- </div>
- <div class="codeLayout">
- <div class="pad">
- <div class="jsFiddle">
- <button class="jsFiddleLink" data-runfiddle="example2">Edit in jsFiddle</button>
- </div>
- <script data-jsfiddle="example2">
- var
- isChecked,
- container2 = document.getElementById('example2'),
- hot2;
- hot2 = new Handsontable(container2, {
- startCols: 2,
- columns: [
- {},
- {renderer: customRenderer}
- ],
- colHeaders: function (col) {
- var txt;
- switch (col) {
- case 0:
- return '"<b>Bold</b> and <em>Beautiful</em>';
- case 1:
- txt = "Some <input type='checkbox' class='checker' ";
- txt += isChecked ? 'checked="checked"' : '';
- txt += "> checkbox";
- return txt;
- }
- }
- });
- function customRenderer(instance, td) {
- Handsontable.renderers.TextRenderer.apply(this, arguments);
- if (isChecked) {
- td.style.backgroundColor = 'yellow';
- }
- else {
- td.style.backgroundColor = 'white';
- }
- return td;
- }
- Handsontable.dom.addEvent(container, 'mousedown', function (event) {
- if (event.target.nodeName == 'INPUT' && event.target.className == 'checker') {
- event.stopPropagation();
- }
- });
- Handsontable.dom.addEvent(container, 'mouseup', function (event) {
- if (event.target.nodeName == 'INPUT' && event.target.className == 'checker') {
- isChecked = !event.target.checked;
- hot2.render();
- }
- });
- </script>
- </div>
- </div>
- </div>
- <div class="rowLayout">
- <div class="descLayout">
- <div class="pad" data-jsfiddle="example2">
- <a name="dropdown"></a>
- <h2>Changing cell type from a dropdown menu in cell header</h2>
- <p>This example makes use of a plugin hook to add a custom dropdown menu to the cell header</p>
- <div id="example3"></div>
- <p>
- <button name="dump" data-dump="#example3" data-instance="hot3" title="Prints current data source to Firebug/Chrome Dev Tools">
- Dump data to console
- </button>
- </p>
- </div>
- </div>
- <div class="codeLayout">
- <div class="pad">
- <div class="jsFiddle">
- <button class="jsFiddleLink" data-runfiddle="example2">Edit in jsFiddle</button>
- </div>
- <style data-jsfiddle="example3">
- .changeType {
- border: 1px solid #bbb;
- color: #bbb;
- background: #eee;
- border-radius: 2px;
- padding: 2px;
- font-size: 9px;
- float: right;
- line-height: 9px;
- margin: 3px 3px 0 0;
- }
- .changeType:hover {
- border: 1px solid #777;
- color: #777;
- cursor: pointer;
- }
- .changeType.pressed {
- background-color: #999;
- }
- .changeTypeMenu {
- position: absolute;
- border: 1px solid #ccc;
- margin-top: 18px;
- box-shadow: 0 1px 3px -1px #323232;
- background: white;
- padding: 0;
- display: none;
- z-index: 10;
- }
- .changeTypeMenu li {
- text-align: left;
- list-style: none;
- padding: 2px 20px;
- cursor: pointer;
- margin-bottom: 0;
- }
- .changeTypeMenu li.active:before {
- content: "\2714";
- margin-left: -15px;
- margin-right: 3px;
- }
- .changeTypeMenu li:hover {
- background: #eee;
- }
- </style>
- <script data-jsfiddle="example3">
- var
- data3 = [
- ['', 'Maserati', 'Mazda', 'Mercedes', 'Mini', 'Mitsubishi'],
- ['2009', 0, 2941, 4303, 354, 5814],
- ['2010', 3, 2905, 2867, 412, 5284],
- ['2011', 4, 2517, 4822, 552, 6127],
- ['2012', 2, 2422, 5399, 776, 4151]
- ],
- columns = [
- {type: 'numeric'},
- {type: 'numeric'},
- {type: 'numeric'},
- {type: 'numeric'},
- {type: 'numeric'},
- {type: 'numeric'}
- ],
- container = document.getElementById('example3'),
- hot3;
- hot3 = new Handsontable(container,{
- data: data3,
- colHeaders: true,
- minSpareRows: 1,
- type: 'numeric',
- columns: columns,
- afterGetColHeader: function (col, TH) {
- var instance = this,
- menu = buildMenu(columns[col].type),
- button = buildButton();
- addButtonMenuEvent(button, menu);
- Handsontable.dom.addEvent(menu, 'click', function (event) {
- if (event.target.nodeName == 'LI') {
- setColumnType(col, event.target.data['colType'], instance);
- }
- });
- TH.firstChild.appendChild(button);
- TH.style['white-space'] = 'normal';
- },
- cells: function (row, col, prop) {
- var cellProperties;
- if (row === 0) {
- cellProperties = {
- type: 'text' // force text type for first row
- };
- return cellProperties;
- }
- }
- });
- function addButtonMenuEvent(button, menu) {
- Handsontable.dom.addEvent(button, 'click', function (event) {
- var changeTypeMenu, position, removeMenu;
- document.body.appendChild(menu);
- event.preventDefault();
- Handsontable.dom.stopImmediatePropagation(event);
- changeTypeMenu = document.querySelectorAll('.changeTypeMenu');
- for (var i = 0, len = changeTypeMenu.length; i < len; i++) {
- changeTypeMenu[i].style.display = 'none';
- }
- menu.style.display = 'block';
- position = button.getBoundingClientRect();
- menu.style.top = (position.top + (window.scrollY || window.pageYOffset)) + 2 + 'px';
- menu.style.left = (position.left) + 'px';
- removeMenu = function (event) {
- if (event.target.nodeName == 'LI' && event.target.parentNode.className.indexOf('changeTypeMenu') !== -1) {
- if (menu.parentNode) {
- menu.parentNode.removeChild(menu);
- }
- }
- };
- Handsontable.dom.removeEvent(document, 'click', removeMenu);
- Handsontable.dom.addEvent(document, 'click', removeMenu);
- });
- }
- function buildMenu(activeCellType){
- var
- menu = document.createElement('UL'),
- types = ['text', 'numeric', 'date'],
- item;
- menu.className = 'changeTypeMenu';
- for (var i = 0, len = types.length; i< len; i++) {
- item = document.createElement('LI');
- if('innerText' in item) {
- item.innerText = types[i];
- } else {
- item.textContent = types[i];
- }
- item.data = {'colType': types[i]};
- if (activeCellType == types[i]) {
- item.className = 'active';
- }
- menu.appendChild(item);
- }
- return menu;
- }
- function buildButton() {
- var button = document.createElement('BUTTON');
- button.innerHTML = '\u25BC';
- button.className = 'changeType';
- return button;
- }
- function setColumnType(i, type, instance) {
- columns[i].type = type;
- instance.updateSettings({columns: columns});
- instance.validateCells(function() {
- instance.render();
- });
- }
- </script>
- </div>
- </div>
- </div>
- <div class="footer-text">
- </div>
- </div>
- </div>
- </div>
- </div>
- <div id="outside-links-wrapper"></div>
- </body>
- </html>
|