基本上原始问题可以分为两部分:
- 主要目标/挑战:嵌入(/传输)原始格式的代码-sn-p
(任何类型的代码)在网页的标记中(用于简单的复制/粘贴/编辑,因为没有
编码/转义)
- 在
浏览器
简短的(但)模棱两可的答案是:你不能,......但你可以(非常接近)。
(我知道,这是 3 个相互矛盾的答案,所以请继续阅读......)
(polyglot)(x)(ht)ml 标记语言依赖于(几乎)包装开始/开始和结束/结束标签/字符(序列)之间的所有内容。
因此,要将 任何 种原始代码/sn-p 嵌入到您的标记语言中,总是必须转义/编码类似于字符(-sequence)的每个实例(在该 sn-p 内) ) 这将关闭标记中的包装“容器”元素。 (在这篇文章中,我将其称为rule no 1。)
想想"some "data" here" 或<i>..close italics with '</i>'-tag</i>,很明显应该转义/编码(某些内容)</i 和"(或将容器的引号字符从" 更改为')。
因此,由于第 1 条规则,您不能“只是”在标记中嵌入“任何”未知的原始代码-sn-p。
因为,如果必须在原始 sn-p 中转义/编码 甚至一个 字符,那么该 sn-p 将不再是任何人都可以复制/粘贴/的原始“纯原始代码”在文档的标记中编辑无需进一步考虑。由于实体,它会导致格式错误/非法标记和Mojibake(主要)。
另外,应该 sn-p 包含这样的字符,你仍然需要一些 javascript 来“翻译”那个字符(序列)从(和到)它被转义/编码表示在“网页”中显示 sn-p 正确(用于复制/粘贴/编辑)。
这将我们带到标记语言指定的(某些)数据类型。这些数据类型本质上定义了什么是“有效字符”及其含义(每个标签、属性等):
PCDATA(已解析的字符数据):将扩展实体,必须
转义 <、&amp;(和 > 取决于标记语言/版本)。
大多数标签如body、div、pre 等,还有textarea(直到
HTML5) 属于这种类型。
因此,您不仅需要对所有容器的结束字符序列进行编码
在 sn-p 中,您还必须对所有 <、&amp; (,>) 字符进行编码
(至少)。
不用说,编码/转义这么多字符不在此范围内
目标在标记中嵌入原始 sn-p 的范围。
'..但是 textarea 似乎可以工作...',是的,要么是因为浏览器
错误引擎试图从中做出一些事情,或者因为 HTML5:
-
RCDATA(可替换字符数据):不会处理内部的标签
文本作为标记(但仍受规则 1 约束),因此不需要
编码< (>)。但是实体仍在扩展,因此它们和“模棱两可”
& 符号 (&amp;) 需要特别小心。
当前 HTML5 spec says the textarea is now a RCDATA field 和(引用):
raw text 和 RCDATA 元素中的文本不得包含任何
字符串 "</" 的出现次数 (U+003C LESS-THAN SIGN, U+002F SOLIDUS)
后跟不区分大小写的与标签名称匹配的字符
后跟 U+0009 CHARACTER TABULATION(制表符)之一的元素,
U+000A 换行 (LF), U+000C 换页 (FF), U+000D 回车
(CR)、U+0020 空格、U+003E 大于号 (>) 或 U+002F SOLIDUS (/)。
因此无论如何,textarea 需要一个强大的实体翻译处理程序或
它将最终在实体上 Mojibake!
CDATA(字符数据)不会将文本内的标签视为
标记,不会展开实体。
所以只要原始的 sn-p 代码不违反规则 1(不能
在 sn-p 中有容器关闭字符(序列),这个
需要没有其他转义/编码。
显然这归结为:我们如何最小化仍然需要在 sn-p 的原始源中编码的字符/字符序列的数量以及该字符(序列)可能出现在平均 sn-p 中的次数;这对于处理这些字符的翻译(如果它们发生)的 javascript 来说也很重要。
那么,CDATA 上下文是什么“容器”?
标签的大多数值属性是 CDATA,因此一个可以(ab)使用隐藏输入的值属性 (proof of concept jsfiddle here)。
但是(符合规则 1)这会在原始 sn-p 中使用嵌套引号(" 和 ')创建一个编码/转义问题,并且需要一些 javascript 来获取/翻译并在另一个中设置 sn-p(可见) 元素(或简单地将其设置为文本区域的值)。不知何故,这给了我在 FF 中的实体问题(就像在文本区域中一样)。但这并不重要,因为必须转义/编码嵌套引号的“代价”高于(HTML5)文本区域(引号在源代码中很常见..)。
尝试(ab)使用<![CDATA[<tag>bla & bla</tag>]]>怎么样?
正如 Jukka 在他的扩展答案中指出的那样,这只适用于(罕见的)“真正的 xhtml”。
我想过使用一个脚本标签(在脚本标签内有或没有这样的 CDATA 包装器)和一个包装原始 sn-p 的多行注释 /* */(脚本标签可以有一个 id 和您可以按计数访问它们)。但由于这显然会在原始 sn-p 中引入*/、]]> 和 </script 的转义问题,这似乎也不是一个解决方案。
请将 cmets 中的其他可行“容器”发布到此答案。
顺便说一句,编码或计算- 字符的数量并在注释标签<!-- --> 中平衡它们对于这个目的来说是疯狂的(除了规则1)。
这给我们留下了Jukka K. Korpela's excellent answer:<xmp> 标签似乎是最好的选择!
“被遗忘”的<xmp> 包含CDATA,旨在用于此目的并且确实仍然是in the current HTML 5 spec(并且至少从HTML3.2 开始就是这样);正是我们需要的!它也得到了广泛的支持,即使在 IE6 中也是如此(也就是说……直到它遭受与滚动表体相同的回归)。
注意:正如 Jukka 所指出的,这在真正的 xhtml 或 polyglot 中不起作用(将其视为pre),xmp 标记仍必须遵守规则 1。但这是“唯一”规则。
考虑以下标记:
<!-- ATTENTION: replace any occurrence of </xmp with </xmp -->
<xmp id="snippet-container">
<div>
<div>this is an example div & holds an xmp tag:<br />
<xmp>
<html><head> <!-- indentation col 0!! -->
<title>My Title</title>
</head><body>
<p>hello world !!</p>
</body></html>
</xmp> <!-- note this encoded/escaped tag -->
</div>
This line is also part of the snippet
</div>
</xmp>
上面的代码块展示了一段原始标记,其中<xmp id="snippet-container"> 包含一个(几乎是原始的)代码-sn-p(包含div>div>xmp>html-document)。
注意到这个标记中的编码结束标签了吗?为了遵守第 1 条规则,这被编码/转义)。
因此,嵌入/传输(有时几乎)原始代码已经/似乎解决了。
如何显示/渲染 sn-p(以及编码的 &lt;/xmp>)?
浏览器将(或应该)渲染 sn-p(snippet-container 中的内容)完全您在上面的代码块中看到的方式(浏览器之间存在一些差异,无论是否sn-p 以空行开头)。
包括格式/缩进、实体(如字符串&amp;)、完整标签、cmets 和编码的结束标签&lt;/xmp>(就像在标记中编码一样)。根据浏览器(版本),甚至可以尝试使用属性contenteditable="true" 来编辑这个sn-p(所有这些都没有启用javascript)。做类似textarea.value=xmp.innerHTML 的事情也很容易。
所以你可以... 如果 sn-p 不包含关闭字符序列的容器。
然而,应该一个原始的 sn-p 包含结束字符序列</xmp(因为它是 xmp 本身的一个示例或者它包含一些正则表达式等( /posting)或(例如)pre 只是为了正确呈现 sn-p 的代码(或者看起来如此)。
一个非常初级的jsfiddle example of this here。请注意,即使在 IE6 中,getting/embedding/displaying/retrieving-to-textarea 也能完美运行。但是设置xmp 的innerHTML 揭示了IE 的一些有趣的“智能”行为。小提琴中有更广泛的注释和解决方法。
但现在是重要的踢球者(你只能靠得很近的另一个原因):
就像一个过于简单的例子,想象一下这个兔子洞:
预期的原始代码-sn-p:
<!-- remember to translate between </xmp> and </xmp> -->
<xmp>
<p>a paragraph</p>
</xmp>
好吧,为了遵守规则 1,我们“只”需要对那些 </xmp[> \n\r\t\f\/] 序列进行编码,对吧?
这给了我们以下标记(仅使用一种可能的编码):
<xmp id="container">
<!-- remember to translate between </xmp> and </xmp> -->
<xmp>
<p>a paragraph</p>
</xmp>
</xmp>
嗯.. 我应该拿水晶球还是掷硬币?不,让计算机查看它的系统时钟并声明派生数字是“随机的”。是的,应该这样做..
使用正则表达式 like:xmp.innerHTML.replace(/&lt;(?=\/xmp[> \n\r\t\f\/])/gi, '<');,会将“返回”翻译为:
<!-- remember to translate between </xmp> and </xmp> -->
<xmp>
<p>a paragraph</p>
</xmp>
嗯..这个随机生成器似乎坏了...休斯顿..?
如果您错过了笑话/问题,请从“预期的原始代码-sn-p”开始再次阅读。
等等,我知道,我们(也)需要将 .... 编码为 ....
好的,倒回“预期的原始代码-sn-p”并再次阅读。
不知何故,这一切开始闻起来像the famous hilarious-but-true rexgex-answer on SO,对于精通 mojibake 的人来说,这是一本好书。
也许有人知道一个聪明的算法或解决方案来解决这个问题,但我认为嵌入式原始代码会变得越来越模糊,以至于你最好只对你的<进行正确转义/编码, &amp;(和>),就像世界其他地方一样。
结论:(使用xmp标签)
- 可以使用不包含容器结束字符序列的已知 sn-ps 来完成,
- 我们可以通过仅使用“基本第一级”转义/编码的已知 sn-ps 非常接近原始目标,因此我们不会陷入困境,
- 但最终似乎无法在“生产环境”中可靠地做到这一点,人们可以/应该在不知道的情况下复制/粘贴/编辑“任何未知”的原始 sn-ps /理解含义/规则/兔子洞(取决于您对规则 1 和兔子洞的处理/翻译实施)。
希望这会有所帮助!
PS:
如果你觉得这个解释有用,我会很感激,但我有点认为 Jukka 的答案应该是公认的答案(不应该出现更好的选择/答案),因为他是记得 xmp 标签的人(我忘了多年来,被pre、textarea 等普遍提倡的 PCDATA 元素“分心”。
这个答案起源于解释为什么你不能这样做(使用任何未知的原始 sn-p)并解释一些其他(现已删除)答案在建议文本区域进行嵌入/传输时被忽略的明显缺陷。我已经扩展了我现有的解释,以支持和进一步解释 Jukka 的答案(因为所有实体和 *CDATA 的东西几乎比代码页更难)。