From 96a0f806c2dbed96726f4d012c0e5ccfff8b8822 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Mon, 28 Jul 2025 21:04:22 +0100 Subject: [PATCH] Add no-JavaScript fallback (Fixes #134) --- README.md | 6 +++--- code-input.css | 51 +++++++++++++++++++++++++++++++++++------------- code-input.js | 28 +++++++++++++++++++++----- tests/hljs.html | 6 +++--- tests/i18n.html | 16 +++++++-------- tests/prism.html | 4 ++-- 6 files changed, 76 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 2607e46..7dcfbbb 100644 --- a/README.md +++ b/README.md @@ -122,13 +122,13 @@ The next step is to set up a `template` to link `code-input` to your syntax-high To see a full list of plugins and their functions, please see [plugins/README.md](./plugins/README.md). ### 4. Using the component -Now that you have registered a template, you can use the custom `` element in HTML. If you have more than one template registered, you need to add the template name as the `template` attribute. With the element, using the `language` attribute will add a `language-{value}` class to the `pre code` block. You can now use HTML attributes and events, as well as CSS styles, to make your element as simple or interactive as you like, as if it were a `textarea` element! +Now that you have registered a template, you can use the custom `` element in HTML. I recommend it surrounds a fallback ` ``` *or* ```HTML - < href='https://github.com/WebCoder49/code-input'>code-input</a> + ``` > ⚠️ At the moment, you need to set the `--padding` property rather than `padding` for a `code-input` element's CSS. All other properties should work as normal. diff --git a/code-input.css b/code-input.css index c3ac631..01505a6 100644 --- a/code-input.css +++ b/code-input.css @@ -12,6 +12,9 @@ code-input { top: 0; left: 0; + color: black; + background-color: white; + /* Normal inline styles */ margin: 8px; --padding: 16px; @@ -90,7 +93,7 @@ code-input textarea, code-input pre { /* Move the textarea in front of the result */ -code-input textarea { +code-input textarea:not([code-input-fallback]) { z-index: 1; } code-input pre { @@ -99,7 +102,7 @@ code-input pre { /* Make textarea almost completely transparent, except for caret and placeholder */ -code-input textarea { +code-input textarea:not([code-input-fallback]) { color: transparent; background: transparent; caret-color: inherit!important; /* Or choose your favourite color */ @@ -122,7 +125,7 @@ code-input textarea { outline: none!important; } code-input:has(textarea:focus):not(.code-input_mouse-focused) { - outline: 2px solid black; + outline: 2px solid currentColor; } /* Before registering give a hint about how to register. */ @@ -134,19 +137,30 @@ code-input:not(.code-input_registered) { code-input:not(.code-input_registered)::after { /* Display message to register */ - content: "Use codeInput.registerTemplate to set up."; + content: "No-JavaScript fallback. For highlighting and plugins: as a user use a newer browser/enable JavaScript support; as a developer use codeInput.registerTemplate."; display: block; position: absolute; - bottom: var(--padding, 16px); + bottom: 0; left: var(--padding, 16px); width: calc(100% - 2 * var(--padding, 16px)); + overflow-x: auto; - border-top: 1px solid grey; - outline: var(--padding, 16px) solid white; - background-color: white; + border-top: 1px solid currentColor; + outline-top: 0; + background-color: inherit; + color: inherit; + + margin: 0; + padding: 0; + height: 2em; +} + +code-input:not(.code-input_registered) textarea { + /* Don't overlap with message */ + min-height: calc(100% - var(--padding, 16px) * 2 - 2em); } -code-input:not(.code-input_loaded) pre, code-input:not(.code-input_loaded) textarea { +code-input:not(.code-input_loaded) pre, code-input:not(.code-input_loaded) textarea:not([code-input-fallback]) { opacity: 0; } @@ -203,7 +217,7 @@ code-input:has(pre[dir=rtl]) .code-input_dialog-container .code-input_keyboard-n right: 0; } -code-input:not(:has(textarea:focus)) .code-input_dialog-container .code-input_keyboard-navigation-instructions, +code-input:not(:has(textarea:not([code-input-fallback]):focus)) .code-input_dialog-container .code-input_keyboard-navigation-instructions, code-input.code-input_mouse-focused .code-input_dialog-container .code-input_keyboard-navigation-instructions, code-input .code-input_dialog-container .code-input_keyboard-navigation-instructions:empty { /* When not keyboard-focused / no instructions don't show instructions */ @@ -211,11 +225,20 @@ code-input .code-input_dialog-container .code-input_keyboard-navigation-instruct } /* Things with padding when instructions are present */ -code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused) textarea, -code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code, -code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre { +code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused) textarea, +code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code, +code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre { padding-top: calc(var(--padding, 16px) + 3em)!important; } -code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused) textarea, code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code, code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre { +code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused) textarea, code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code, code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre { min-height: calc(100% - var(--padding, 16px) * 2 - 3em); } + +/* No JavaScript fallback - styles to override all previous */ + +code-input textarea[code-input-fallback] { + overflow: auto; + background-color: inherit; + color: inherit; + height: max-content; +} diff --git a/code-input.js b/code-input.js index 82e92e3..4b391c8 100644 --- a/code-input.js +++ b/code-input.js @@ -605,16 +605,34 @@ var codeInput = { this.pluginEvt("beforeElementsAdded"); + const fallbackTextarea = this.querySelector("textarea[code-input-fallback]"); + let value; + if(fallbackTextarea) { + // Fallback textarea exists + // Sync attributes; existing code-input attributes take priority + let textareaAttributeNames = fallbackTextarea.getAttributeNames(); + for(let i = 0; i < textareaAttributeNames.length; i++) { + const attr = textareaAttributeNames[i]; + if(!this.hasAttribute(attr)) { + this.setAttribute(attr, fallbackTextarea.getAttribute(attr)); + } + } + // Sync value + value = fallbackTextarea.value; + } else { + value = this.unescapeHtml(this.innerHTML); + } + value = value || this.getAttribute("value") || ""; + // First-time attribute sync - let lang = this.getAttribute("language") || this.getAttribute("lang"); - let placeholder = this.getAttribute("placeholder") || this.getAttribute("language") || this.getAttribute("lang") || ""; - let value = this.unescapeHtml(this.innerHTML) || this.getAttribute("value") || ""; - // Value attribute deprecated, but included for compatibility + const lang = this.getAttribute("language") || this.getAttribute("lang"); + const placeholder = this.getAttribute("placeholder") || lang || ""; + this.initialValue = value; // For form reset // Create textarea - let textarea = document.createElement("textarea"); + const textarea = document.createElement("textarea"); textarea.placeholder = placeholder; if(value != "") { textarea.value = value; diff --git a/tests/hljs.html b/tests/hljs.html index 1aeb729..ffa1cbc 100644 --- a/tests/hljs.html +++ b/tests/hljs.html @@ -42,9 +42,9 @@

Test for Prism.js

Test Results (Click to Open)
- console.log("Hello, World!"); + @@ -52,4 +52,4 @@

Test for Prism.js

beginTest(true); - \ No newline at end of file + diff --git a/tests/i18n.html b/tests/i18n.html index 8f18f80..ed6321d 100644 --- a/tests/i18n.html +++ b/tests/i18n.html @@ -48,14 +48,14 @@ - - - - - - - - + + + + + + + +