【问题标题】:Partially run code as html and as text部分运行代码为 html 和文本
【发布时间】:2018-06-03 05:33:36
【问题描述】:

简介

我目前正在创建一个模板构建器,用户可以在其中为应用构建模板。用户可以拖放多个块,例如文本块和“自定义代码”块。模板将在应用程序中解析。现在,模板可能如下所示:

<section>
    <div class="row">
        <div class="col-sm-12">
            <section data-type="code">
                <#code></#code>
            </section>
        </div>
    </div>
    <div class="row">
        <div class="col-sm-12" data-type="container-content">
            <section data-type="text">
                <u>Lorem</u> ipsum
            </section>
        </div>
    </div>
</section>

所以,这个模板包含两个元素(参见data-type 属性):一部分是自定义编写的代码。在这里,用户编写了自定义代码,包括 Apache Freemarker 代码。第二部分是自定义书面文本。

情况

上面的代码将以两种不同的方式使用。

  • 确切地说,此代码将在使用 模板(这就是他们应该能够编写 Freemarker 代码的原因, 因为这将被解析)。
  • 在我的网站上,用户应该能够 编辑此模板。因为代码存储在数据库中 上面写的,有问题:

问题

当我直接在 Web 界面中呈现模板时,text 部分将使用 &lt;u&gt;&lt;/u&gt; 标签正确呈现,但 code 部分也将呈现为 html,这可能会导致奇怪的行为(例如作为 freemarker 符号 &lt;/#list&gt; 被自动转换为 &lt;!--#list--&gt;)。

但是,如果我仅将完整模板呈现为文本,则带有 &lt;u&gt;&lt;/u&gt; 标签的 text 部分也不会被呈现。

预期结果

我想用 JavaScript / jQuery 读取模板变量,然后将每个 data-type 解析为 html,将 text 解析为 html,并将 code 解析为文本。

如何循环遍历模板并执行此操作?

【问题讨论】:

  • 这很有趣......这整个事情是在客户端编译和运行的吗?
  • 这可能应该在 html 在浏览器端呈现之前完成
  • @Soolie 是的,是的!
  • 不应该$('[data-type="code"]').each(function () {$(this).text($(this).html())}); 做这个伎俩吗?
  • 否,因为$('[data-type="code"]') 仅适用于已解析为 html 的模板。当我这样做时,代码部分将被解析,这就是我想要阻止的。

标签: javascript jquery html templates escaping


【解决方案1】:

还有一种替代语法使用方括号代替尖括号。

检查它是否解决了您的标签识别问题而不会弄乱任何其他功能。

https://freemarker.apache.org/docs/dgui_misc_alternativesyntax.html

编辑 1

要在解析 HTML 时在 标记内显示源代码,您可以在数据库中对其进行转义(将 和 & 等 html 特殊字符转义为 < > 和 &)。所以,在渲染的时候,不会在代码内容中创建html标签,文档也不会乱七八糟。

然后,您可以直接将数据库的所有内容呈现为 HTML:文本将保留标记,代码将是文本。

要进行该修改,您可以使用正则表达式来查找 标记所包含的内容并替换为 HTML 转义的等效项。执行此操作的确切方法取决于您将用于该工作的语言,因为 RegEx 和可用的转义函数存在一些差异。

编辑 2

如果您使用 AJAX 加载内容,您有机会在 javascript 中应用替换,在从服务器获取内容之后,保持数据库不变。

【讨论】:

  • 是的,好点子!这肯定解决了尖括号的问题。有什么想法可以完全阻止&lt;section date-type="code"&gt;&lt;/section&gt;之间的代码执行吗?
  • 是否可以选择转义数据库中已有的代码?我发现很难恢复可能已经渲染的 html 代码,最终弄乱了以下标签。
  • 我认为这可能是我唯一的选择。所以我必须这样做,或者找到另一种解决方法。
【解决方案2】:

问题回顾

在javascript中解析HTML,一般使用DOMParser对象(IE10+支持)。

如你所说,data-type="code" 部分内的解析失败,因为它不知道如何处理 &lt;/#...&gt; 标签...

const templ = `<section><div class="row"><div class="col-sm-12"><section data-type="code"><#code></#code></section></div></div><div class="row"><div class="col-sm-12" data-type="container-content"><section data-type="text"><u>Lorem</u> ipsum</section></div></div></section>`;
const parser = new DOMParser();
const doc = parser.parseFromString(templ, "text/html");

console.log(
  "Wrongly parsed </#code> tag:\n",
  doc.querySelector("[data-type='code']").innerHTML
);

寻找解决办法

现在,尝试对需要转义的字符进行快速正则表达式查找和替换,这听起来可能是个好主意,but I wouldn't recommend it...

据我所知,没有办法“闯入”解析过程或为某些类型的元素传递策略...

我想说这给你留下了两个选择。要么:

  1. 不要在代码部分使用不可解析的语法,正如用户Eduardo Poçotheir answer 中所建议的那样

或者,(我喜欢的方向),尝试

  1. 修改模板本身以停止解析代码部分的内容

使用修改后的模板

HTML 中有一个类似“脚本”的标签!不出所料,它是&lt;script&gt; 标签。让我们将它注入我们的code 部分:

<section data-type="code">
    <script type="text">
        <#code></#code>
    </script>
</section>

DOMParser 不会触及这个标签,保持原样:

const templ = '<section><div class="row"><div class="col-sm-12"><section data-type="code"><script type="text"><#code></#code></' + 'script></section></div></div><div class="row"><div class="col-sm-12" data-type="container-content"><section data-type="text"><u>Lorem</u> ipsum</section></div></div></section>';

const parser = new DOMParser();
const doc = parser.parseFromString(templ, "text/html");

console.log(
  "Now, there's a <script> tag:\n",
  doc.querySelector("[data-type='code']").innerHTML
);

请注意,我必须将模板字符串从两部分连接起来,以确保 stackoverflow 的 sn-p 不会中断。他们是否遇到类似的问题? :-o


现在,我们要做的就是使用通用的 DOM 方法,包括 innerTextnot innerHTML)将脚本的内部内容返回到 DOM 的可见部分:

var templ = '<section><div class="row"><div class="col-sm-12"><section data-type="code"><script type="text"><#code></#code></' + 'script></section></div></div><div class="row"><div class="col-sm-12" data-type="container-content"><section data-type="text"><u>Lorem</u> ipsum</section></div></div></section>`;'

var parser = new DOMParser();
var doc = parser.parseFromString(templ, "text/html");

Array
  .from(doc.querySelectorAll(
    "[data-type='code'] > script")
  )
  .forEach(script => {
      const codeTag = document.createElement("code");
      codeTag.innerText = script.innerHTML;
      script.replaceWith(codeTag);
  });

document.getElementById("wrapper").appendChild(doc.body.firstChild);
code { background: #efefef; }
&lt;div id="wrapper"&gt;&lt;/div&gt;

【讨论】:

    【解决方案3】:

    您可以使用字符集代码,因此它在输出之前不会执行。 HTML charset reference

    他们可以编辑它,因为它看起来很正常并将其发送回您或服务器。确保在头部包含您的字符集参考。

    <meta charset="UTF-8"> // HTML5 
    <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"> // HTML4
    

    <!-- @CHARSET / No execute -->
    &#60;section&#62;
        &#60;div class="row"&#62;
            &#60;div class="col-sm-12"&#62;
                &#60;section data-type="code"&#62;
                    <#code> <!-- Run this --> </#code>
                &#60;/section&#62;
            &#60;/div&#62;
        &#60;/div&#62;
        &#60;div class="row"&#62;
            &#60;div class="col-sm-12" data-type="container-content"&#62;
                &#60;section data-type="text"&#62;
                    &#60;u&#62; <!-- Don't run --> &#60;/u&#62; 
                &#60;/section&#62;
            &#60;/div&#62;
        &#60;/div&#62;
    &#60;/section&#62;

    【讨论】:

    • 但是,一旦我解析它,所有代码都会被执行,所以它没有任何意义。我想要的是执行所有代码,&lt;section date-type="code"&gt;&lt;/section&gt; 标签之间的代码除外。
    【解决方案4】:

    如果我没理解错的话,你可以使用&lt;plaintext&gt;标签将块渲染为页面上的文本。

    <plaintext>
        <#code></#code>
    </plaintext>
    

    【讨论】:

    • 不幸的是它已经过时了。而且它没有结束标签,所以我猜它会将标签之后的所有代码呈现为文本。
    【解决方案5】:

    这样的事情呢?

    // https://stackoverflow.com/questions/7477/autosizing-textarea-using-prototype
    function FitToContent(id, maxHeight)
    {
       var text = id && id.style ? id : document.getElementById(id);
       if (!text)
          return;
    
       /* Accounts for rows being deleted, pixel value may need adjusting */
       if (text.clientHeight == text.scrollHeight) {
          text.style.height = "30px";
       }
    
       var adjustedHeight = text.clientHeight;
       if (!maxHeight || maxHeight > adjustedHeight)
       {
          adjustedHeight = Math.max(text.scrollHeight, adjustedHeight);
          if (maxHeight)
             adjustedHeight = Math.min(maxHeight, adjustedHeight);
          if (adjustedHeight > text.clientHeight)
             text.style.height = adjustedHeight + "px";
       }
    }
    
    $('textarea').each(function(i,v){
      FitToContent($(v)[0], document.documentElement.clientHeight)
    });
    textarea {
      background: transparent;
      border: 0;
      font-family: 'Times New Roman';
      font-size: 1em;
      width: 100%;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <section>
        <div class="row">
            <div class="col-sm-12">
                <h4>First code block</h4>
                <section data-type="code">
                    <textarea class='code'><#code>
        <h2>FreeMarker Spring MVC Hello World</h2>
        <table class="datatable">
            <tr>
                <th>Make</th><th>Model</th>
            </tr>
            <#list model["carList"] as car>
            <tr>
                <td>${car.make}</td>
                <td>${car.model}</td>
            </tr>
            </#list>
        </table>
    </#code></textarea>
                </section>
            </div>
        </div>
        <div class="row">
            <div class="col-sm-12" data-type="container-content">
                <h4>Regular HTML section</h4>
                <section data-type="text">
                    <u>Lorem</u> ipsum
                </section>
            </div>
        </div>
          <div class="row">
            <div class="col-sm-12">
                <h4>Second code block</h4>
                <section data-type="code">
                    <textarea class='code'><#code>
        <table class="datatable">
            <tr>
                <th>Name</th><th>Email</th>
            </tr>
            <#list model["personList"] as person>
            <tr>
                <td>${person.name}</td>
                <td>${person.email}</td>
            </tr>
            </#list>
        </table>
    </#code></textarea>
                </section>
            </div>
        </div>
    </section>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-28
      • 2014-04-24
      • 2020-01-08
      • 2016-04-09
      相关资源
      最近更新 更多