This commit is contained in:
SheetJS 2023-11-30 02:16:08 -05:00
commit c66017e0e0
2 changed files with 513 additions and 0 deletions

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

513
index.html Normal file

@ -0,0 +1,513 @@
<span style='
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;
-webkit-font-smoothing: antialiased;
display: block; max-width: 720px; margin-left: auto; margin-right: auto;
'>
<span contenteditable autofocus onfocus="(function(){
document.title = 'OnlySpans.net';
[...document.getElementsByTagName('SPAN')].forEach(function(n) {
/* send a synthetic 'dblclick' event to each span */
var evt = document.createEvent('MouseEvents');
evt.initEvent('dblclick', true, true);
n.dispatchEvent(evt);
/* NOTE: styles and event handler can be added inline. These are added in
an event handler to demonstrate how to emulate CSS classes */
var S = n.style;
/* configure 'details' spans */
if(n.classList.contains('details')) {
/* styling */
S['font-weight'] = 700;
S['background-color'] = '#54c7ec26';
S['border-radius'] = '5px';
S['padding'] = '5px';
S['border'] = '1px solid #4cb3d4';
S['display'] = 'inline-block';
n.innerHTML = '&#x25B6; Code (click to show)';
/* click handler */
n.addEventListener('click', function(){
if(/click to hide/.test(n.innerText)) {
n.nextElementSibling.style.display = 'none';
n.innerHTML = '&#x25B6; Code (click to show)';
} else {
n.nextElementSibling.style.display = 'inline-block';
n.innerHTML = '&#x25BC; Code (click to hide)';
}
});
}
/* configure 'codeblock' spans */
if(n.classList.contains('codeblock')) {
S['padding'] = '5px';
S['font-family'] = 'monospace';
S['display'] = 'none';
S['background-color'] = '#efefef';
S['border-radius'] = '5px';
S['border'] = '1px solid #d4d5d8';
}
/* configure spans with 'href' attribute */
if(n.hasAttribute('href')) {
S['white-space'] = 'pre-wrap';
S['cursor'] = 'pointer';
S['color'] = 'blue';
n.addEventListener('click', function() {
window.location.href = n.getAttribute('href');
});
}
/* configure spans with 'heading' role */
if(n.hasAttribute('role')) {
switch(n.getAttribute('role')) {
case 'heading': switch(n.getAttribute('aria-level')) {
case '2': {
S['font-weight'] = '700';
S['font-size'] = '1.5rem';
} break;
case '3': {
S['font-weight'] = '700';
S['font-size'] = '1.25rem';
S['white-space'] = 'pre-wrap';
} break;
} break;
}
}
});
/* hide element (this probably should be removed) */
event.target.style.display = 'none';
})()"></span>
<span role="heading" aria-level="1" style="
white-space: pre;
font-weight: 700;
font-size: 2rem;
width: 100%;
text-align: center;
display: block;
">Only &lt;SPAN&gt;s</span>
<span style="
white-space: pre-wrap;
">
Each element on this page is a SPAN element. No elements are dynamically added to the page.
</span>
<span style="
white-space: pre-wrap;
">
<span><span role="heading" aria-level="2">Styles
</span>
<span>Each SPAN specifies styling properties through the `style` attribute.</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
">This is <span style="font-weight: 700">bold</span>
This is <span style="font-style: italic">italicized</span>
<span style="text-decoration: line-through">These effects are impossible</span>
<span><span class="details">Show Code</span>
<span class="codeblock">```html
This is &lt;span style="font-weight: 700"&gt;bold&lt;/span&gt;
This is &lt;span style="font-style: italic"&gt;italicized&lt;/span&gt;
&lt;span style="text-decoration: line-through"&gt;These effects are impossible&lt;/span&gt;
```</span></span></span>
<span><span role="heading" aria-level="3">Line Breaks</span></span>
Newline characters are normally treated like spaces. They do not force a line break. `BR` elements force line breaks.
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
"><span style="white-space: normal;">Normally, newline characters
do not actually break lines. They
are treated like space characters.</span>
<span><span class="details">Show Code</span>
<span class="codeblock">```html
&lt;span style="white-space: normal;"&gt;Normally, newline characters
do not actually break lines. They
are treated like space characters.&lt;/span&gt;
```</span></span></span>
<span>When SPAN elements have the styling property <span style="font-family: monospace">white-space: pre-wrap</span>, newline characters in the HTML code are treated as line breaks.</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
"><span style="white-space: pre-wrap;">With pre-wrap, newline characters
actually break lines and
are not treated like space characters</span>
<span><span class="details">Show Code</span>
<span class="codeblock">```html
&lt;span style="white-space: pre-wrap;"&gt;With pre-wrap, newline characters
actually break lines and
are not treated like space characters&lt;/span&gt;
```</span></span></span></span>
<span><span role="heading" aria-level="3">CSS Tables</span>
<span style="
white-space: pre-wrap;
">CSS Level 3 supports special values for the `display` styling property:</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
"><span style="display: table">
<span style="display: table-row">
<span style="display: table-cell; padding: 3px;"><span style="font-weight: 700; text-align:center; display:inline-block; width: 100%">Value</span></span>
<span style="display: table-cell; padding: 3px;"><span style="font-weight: 700; text-align:center; display:inline-block; width: 100%">Behavior</span></span>
</span>
<span style="display: table-row">
<span style="display: table-cell; padding: 3px;"><span style="font-family:monospace; width: 100%">table</span></span>
<span style="display: table-cell; padding: 3px;"><span style="width: 100%">Acts like <span style="font-family:monospace">TABLE</span></span></span>
</span>
<span style="display: table-row">
<span style="display: table-cell; padding: 3px;"><span style="font-family:monospace; width: 100%">table-row</span></span>
<span style="display: table-cell; padding: 3px;"><span style="width: 100%">Acts like <span style="font-family:monospace">TR</span></span></span>
</span>
<span style="display: table-row">
<span style="display: table-cell; padding: 3px;"><span style="font-family:monospace; width: 100%">table-cell</span></span>
<span style="display: table-cell; padding: 3px;"><span style="width: 100%">Acts like <span style="font-family:monospace">TD</span></span></span>
</span>
</span>
<span><span class="details">Show Code</span>
<span class="codeblock">```html
&lt;span style="display: table"&gt;
&lt;span style="display: table-row"&gt;
&lt;span style="display: table-cell"&gt;Value&lt;/span&gt;
&lt;span style="display: table-cell"&gt;Behavior&lt;/span&gt;
&lt;/span&gt;
&lt;span style="display: table-row"&gt;
&lt;span style="display: table-cell"&gt;table&lt;/span&gt;
&lt;span style="display: table-cell"&gt;Acts like TABLE&lt;/span&gt;
&lt;/span&gt;
&lt;/span&gt;
```</span></span></span>
<span>Using these special style properties, SPAN elements can create simple table layouts. Unfortunately there is no equivalent of `colspan` and `rowspan` ("merge cells" in Excel parlance)
</span></span>
<span><span role="heading" aria-level="3">Images</span>
<span style="
white-space: pre-wrap;
">Images can be added to pages using the `background-image` inline style. To ensure spans are displayed, the `width`, `height` and `display` attributes are set:
</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
"><span style="
width: 128px;
height: 128px;
background-image: url('https://sheetjs.com/sketch128.png');
display:inline-block;
"></span>
<span><span class="details">Show Code</span>
<span class="codeblock">```html
&lt;span style="
width: 128px;
height: 128px;
background-image: url('https://sheetjs.com/sketch128.png');
display: inline-block;
"&gt;&lt;/span&gt;
```</span></span></span>
</span></span>
<span style="
white-space: pre-wrap;
">
<span><span role="heading" aria-level="2">Dynamic Elements</span>
<span>Inline event handlers allow for some scripting without introducing explicit SCRIPT tags.</span>
<span>The following example uses the `onclick` inline event handler to increment a counter:</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
">The blue span has been clicked <span style="
font-family: monospace;
font-weight:700;
background-color: #54c7ec26;
border-radius: 5px;
padding:5px; border: 1px solid #4cb3d4;
display: inline-block;
" onclick="(function() {
event.target.innerText = (+event.target.innerText) + 1;
})()">0</span> times
<span><span class="details">Show Code</span>
<span class="codeblock">```html
&lt;span onclick="(function() {
event.target.innerText = (+event.target.innerText) + 1;
})()"&gt;0&lt;/span&gt;
```</span></span></span>
<span><span role="heading" aria-level="3">Effectuating `onload`</span></span>
<span>SPAN elements do not receive a `load` event. Instead, this page uses a `contenteditable` SPAN with an `autofocus` attribute. This ensures the `onfocus` inline handler is called.</span>
<span>If multiple elements have the `autofocus` attribute, only the first element will receive focus.</span>
<span>To simplify the page, the `contenteditable autofocus` SPAN will send a synthetic double click (`dblclick`) event to each SPAN element on the page before hiding itself:</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
">The relevant SPAN is at the top of the page (line 6 in the source). If it were placed here, the browser would scroll to this SPAN.
<span><span class="details">Show Code</span>
<span class="codeblock">```html
&lt;span contenteditable autofocus onfocus="(function(){
[...document.getElementsByTagName('SPAN')].forEach(function(n) {
var evt = document.createEvent('MouseEvents');
evt.initEvent('dblclick', true, true);
n.dispatchEvent(evt);
});
event.target.style.visibility = 'hidden';
})()"&gt;&lt;/span&gt;
```</span></span></span>
<span>Within the `ondblclick` inline handler, the SPAN DOM element can be referenced with the `<span style="font-family: monospace">target</span>` property of the `<span style="font-family: monospace">event</span>` global.</span>
<span>The following example shows the current time. The `ondblclick` event handler uses `setInterval` to set up a loop that updates the time. Clearing the `ondblclick` property ensures that a real double click will not invoke the original handler.</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
">The time is now <span style="
font-family: monospace;
font-weight:700;
background-color: #efefef;
border-radius: 5px;
padding:5px; border: 1px solid #d4d5d8;
display: inline-block;
" ondblclick="(function() {
var last = new Date().toString(), tgt = event.target;
tgt.innerText = last;
setInterval(function() {
var cur = new Date().toString();
if(last != cur) tgt.innerText = last = cur;
}, 100);
tgt.ondblclick = null;
})()"></span>
<span><span class="details">Show Code</span>
<span class="codeblock">```html
&lt;span style="font-family: monospace" ondblclick="(function() {
var last = new Date().toString(), tgt = event.target;
tgt.innerText = last;
setInterval(function() {
var cur = new Date().toString();
if(last != cur) tgt.innerText = last = cur;
}, 100);
tgt.ondblclick = null;
})()"&gt;&lt;/span&gt;
```</span></span></span></span>
<span><span role="heading" aria-level="3">Emulating CSS Classes</span>
<span>Within the `onfocus` handler of the `autofocus` SPAN, every SPAN element on the page is visited. The `classList` property is a list of class names. Code can manually apply styling properties.</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
">The code blocks that are displayed with the "Show Code" buttons use the `codeblock` class.
<span><span class="details">Show Code</span>
<span class="codeblock">```html
&lt;span contenteditable autofocus onfocus="(function(){
[...document.getElementsByTagName('SPAN')].forEach(function(n) {
/* configure 'codeblock' spans */
if(n.classList.contains('codeblock')) {
var S = n.style;
S['font-family'] = 'monospace';
S['display'] = 'none';
}
})()"&gt;&lt;/span&gt;
```</span></span></span></span>
<span><span role="heading" aria-level="3">Fetching Data</span>
<span>The `ondblclick` handler can fetch data. The following example fetches a text file:</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
">The current version of <span href="https://sheetjs.com" role="link">SheetJS Community Edition</span> is <span style="
font-family: monospace;
font-weight:700;
background-color: #efefef;
border-radius: 5px;
padding:5px;
border: 1px solid #d4d5d8;
display: inline-block;
" ondblclick="(async function() {
const versions = await (await fetch('https://cdn.sheetjs.com/xlsx.lst')).text();
event.target.innerText = versions.trim().split('\n').reverse()[0];
})()"></span>
<span><span class="details">Show Code</span>
<span class="codeblock">```html
&lt;span ondblclick="(async function() {
const versions = await (await fetch('https://cdn.sheetjs.com/xlsx.lst')).text();
event.target.innerText = versions.trim().split('\n').reverse()[0];
})()"&gt;&lt;/span&gt;
```</span></span></span></span>
<span><span role="heading" aria-level="3">Editable Elements</span>
<span>The `contenteditable` attribute allows users to edit the content of SPAN elements. An `input` event is dispatched when the content has changed.</span>
<span>The following example includes one `contenteditable` SPAN and one non-editable SPAN. The `oninput` event handler of the first SPAN changes the `innerText` property of the second SPAN.
</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
">Click the text span below and start typing:
<span contenteditable style="background-color: white; border-radius: 5px; padding:5px; border: 1px solid #009400; display: inline-block;" oninput="(function() {
/* output is the ID of the span that will be changed */
output.innerText = event.target.innerText||' ';
})()">Click here and start typing!</span>
The following span will update when the previous span is edited:
<span><span id="output" style="background-color: #efefef; border-radius: 5px; padding:5px; border: 1px solid #d4d5d8; display: inline-block;">Edit the previous span</span></span></span></span>
<span><span role="heading" aria-level="3">External Scripts</span>
<span>External scripts can be fetched and evaluated using `eval`.</span>
<span>The following example fetches the <span href="https://docs.sheetjs.com/docs/constellation/ssf" role="link">SheetJS SSF Library</span> and displays the version number.
</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
">The current version of the SheetJS SSF number formatting library is <span style="
font-family: monospace; font-weight:700;
background-color: #efefef;
border-radius: 5px;
padding: 5px;
border: 1px solid #d4d5d8;
display: inline-block;
" ondblclick="(async function() {
const url = 'https://cdn.sheetjs.com/ssf-0.11.3/ssf.js';
const code = await (await fetch(url)).text();
eval(code);
window.SSF = SSF;
event.target.innerText = SSF.version;
})()"></span>
<span><span class="details">Show Code</span>
<span class="codeblock">```html
&lt;span ondblclick="(async function() {
const url = 'https://cdn.sheetjs.com/ssf-0.11.3/ssf.js';
const code = await (await fetch(url)).text();
eval(ssf_text);
event.target.innerText = SSF.version;
})()"&gt;&lt;/span&gt;
```</span></span></span>
<span>This example uses the <span href="https://docs.sheetjs.com/docs/constellation/ssf" role="link">SheetJS SSF Library</span> to format values. The SPAN elements with green borders are editable. When either SPAN is changed, the "Number Value" SPAN content is interpreted as a JS `Number` and formatted using the Format Code.</span>
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #efefef;
border-radius: 10px;
">The "Formatted Text" is automatically calculated from the "Format Code" and "Number Value". Try setting the "Format Code" to <span style="font-family: monospace; font-weight: 700">#,##0.00</span> and the value to <span style="font-family: monospace; font-weight: 700">314159.265</span>
<span style="display: table">
<span style="display: table-row">
<span style="display: table-cell; padding: 3px;"><span style="font-weight: 700; display:inline-block; width: 100%">Format Code</span></span>
<span style="display: table-cell; padding: 3px;"><span id="fmt" contenteditable style="font-family: monospace; width: 100%; background-color: white; border-radius: 5px; padding:5px; border: 1px solid #009400; display: inline-block; min-width: 11ch;" oninput="(function() {
var evt = document.createEvent('MouseEvents');
evt.initEvent('dblclick', true, true);
outssf.dispatchEvent(evt);
})()">$0.00</span></span>
</span>
<span style="display: table-row">
<span style="display: table-cell; padding: 3px;"><span style="font-weight: 700; display:inline-block; width: 100%">Number Value</span></span>
<span style="display: table-cell; padding: 3px;"><span id="val" contenteditable style="font-family: monospace; width: 100%; background-color: white; border-radius: 5px; padding:5px; border: 1px solid #009400; display: inline-block;" oninput="(function() {
var evt = document.createEvent('MouseEvents');
evt.initEvent('dblclick', true, true);
outssf.dispatchEvent(evt);
})()">3.5</span></span>
</span>
<span style="display: table-row"><span style="display: table-cell;">&nbsp;</span></span>
<span style="display: table-row">
<span style="display: table-cell; padding: 3px;"><span style="font-weight: 700; display:inline-block; width: 100%">Formatted Text</span></span>
<span style="display: table-cell; padding: 3px;"><span id="outssf" style="font-family: monospace; font-weight: 700; width: 100%; background-color: #54c7ec26; border-radius: 5px; padding:5px; border: 1px solid #4cb3d4; display: inline-block;" ondblclick="(function() {
if(typeof SSF == 'undefined') return;
/* simple debounce wrapper */
function debounce(f) {
let id;
return function() {
if(id) window.cancelAnimationFrame(id);
id = window.requestAnimationFrame(() => f.call(this, ...arguments));
}
}
debounce(() => {
const v = Number(val.innerText), S = outssf.style;
try {
if(!isFinite(v)) throw 'Could not parse ' + val.innerText + ' as a finite number!';
outssf.innerText = SSF.format(fmt.innerText, Number(val.innerText));
S['background-color'] = '#54c7ec26';
S['border-color'] = '#4cb3d4';
} catch(e) {
outssf.innerText = 'ERROR: ' + (e && e.message || e);
S['background-color'] = '#fa383e26';
S['border-color'] = '#e13238';
}
})();
})()">$3.50</span></span>
</span>
</span></span></span>
</span>
<span style="
white-space: pre-wrap;
">
<span style="
margin: 5px;
padding: 15px;
display: block;
background-color: #e6f6e6;
border-radius: 10px;
"><span href="https://sheetjs.com" role="link">SheetJS LLC</span> has been "making sheet happen" with creative use of HTML, CSS and JS since 2012. If pushing the limits of the web tickles your fancy, <span href="https://sheetjs.com/careers" role="link">consider joining us!</span></span>
</span>
</span>