限制内联元素的长度

时间:2011-08-03 20:54:14

标签: c# css string xslt

如何限制可变长度文本元素的长度,该文本元素可以包含文本属性(<b>, <i>, <sup>, ...)和链接。虽然在适当的位置可以删除整个标签(打开和关闭)(不能删除所有标签以简化问题),但是标签需要保持打开和关闭。我有c#,xslt和css可供我使用。我不想用javascript做这件事。

例如:

On the <b>approximate realization</b> of continuous mappings by <i>neural networks</i> <a href='http://...very long link'>some text</a>...

请记住,标签本身(及其属性)不应计入长度。

此外,文本应该换行,因此使用宽度和溢出是不可能的。

Mrchief和Dimitre Novatchev都有很好的解决方案。我更喜欢把这个逻辑放在我的xslt中,所以我选择Dimitre Novatchev作为答案,尽管两者都应该是。

3 个答案:

答案 0 :(得分:2)

这是一个尝试的解决方案:

    public static string LimitText(string input, int width)
    {
        const string pattern = @"(</?[a-zA-Z0-9 '=://.]+>)";  

        var rgx = new Regex(pattern, RegexOptions.Compiled);  

        // remove tags and chop text to set width
        var result = rgx.Replace(input, string.Empty).Substring(0, width);

        // split till word boundary (so that "shittake" doesn't end up as "shit")
        result = result.Substring(0, result.LastIndexOf(' '));

        var matches = rgx.Matches(input);

        // non LINQ version to keep things simple
        foreach (Match match in matches)
        {
            var groups = match.Groups;
            if (groups[0].Index > result.Length) break;
            result = result.Insert(groups[0].Index, groups[0].Value);
        }

        // check for unbalanced tags
        matches = rgx.Matches(result);

        if (matches.Count % 2 != 0)
        {
            // chop off unbalanced tag
            return result.Substring(0, matches[matches.Count-1].Groups[0].Index);
        }

        return result;
    }

<强>注意事项:

  1. 正则表达式匹配帖子中指定的所有标签。您可以根据实际情况对其进行扩展以包含更多字符。但是,解析 使用Regex的HTML总是很棘手。
  2. 如果您的输入字符串不包含平衡标记(即对于每个开始标记,则存在结束标记),这可能无法按预期工作。
  3. 如果您希望输入字符串中包含自动结束标记(例如<br />)或打开input标记,则需要进行飞行前清理。想法是一样的,得到一组匹配,运行LimitText并在结果字符串上重新插入这些标记。
  4. 浏览器上的最终呈现文本可能仍然不能令人满意,因为字体大小或屏幕分辨率可能会产生不正确的结果。为此,您需要使用基于JS的解决方案。
  5. 这应该让你开始,然后你可以扩展它以适应任何极端情况。

答案 1 :(得分:2)

此XSLT 1.0转换

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kTextById" match="text()" use="generate-id()"/>

 <xsl:param name="pMaxLength" select="60"/>

 <xsl:variable name="vTextToSplit">
  <xsl:apply-templates select="(//text())[1]" mode="calc"/>
 </xsl:variable>



 <xsl:variable name="vsplitNode" select=

     "key('kTextById', substring-before(substring-after($vTextToSplit,'|'), '|'))"/>


 <xsl:variable name="vsplitLength" select=
     "substring-before($vTextToSplit,'|')"/>

 <xsl:variable name="vsplitPos" select=
     "substring-after(substring-after($vTextToSplit,'|'),'|')"/>

 <xsl:template match="node()|@*">

  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/">
  <xsl:choose>
   <xsl:when test="not($vTextToSplit)">
    <xsl:copy-of select="."/>
   </xsl:when>

   <xsl:otherwise>

     <xsl:apply-templates select="/node()"/>

   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>



 <xsl:template match="text()" mode="calc">
  <xsl:param name="paccumLength" select="0"/>


  <xsl:variable name="vPos" select="count(preceding::text())+1"/>


  <xsl:variable name="vnewAccumLength" select=
               "$paccumLength+string-length()"/>

  <xsl:choose>
   <xsl:when test="$vnewAccumLength >= $pMaxLength">
     <xsl:value-of select=
      "concat(string-length() - ($vnewAccumLength -$pMaxLength),

              '|', generate-id(),
              '|', $vPos

              )"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:apply-templates mode="calc"
      select="(//text())[position() = $vPos+1]">
     <xsl:with-param name="paccumLength" select="$vnewAccumLength"/>
    </xsl:apply-templates>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>


 <xsl:template match="text()">
  <xsl:variable name="vPos" select="count(preceding::text())+1"/>

  <xsl:choose>
   <xsl:when test="$vPos > $vsplitPos"/>
   <xsl:when test="$vPos = $vsplitPos">
    <xsl:value-of select="substring(.,1,$vsplitLength)"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:value-of select="."/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

应用于提供的输入(包装到单个顶部元素中以使其成为格式良好的XML文档):

<t>On the <b>approximate realization</b> of continuous mappings by <i>neural networks</i> <a href='http://...very long link'>some text</a>...</t>

生成所需的正确结果 - 格式良好的XML文档,其中包含源XML文档的元素,其文本节点的总长度与指定的长度完全相等(60 )在全局参数$pMaxLength

<t>On the <b>approximate realization</b> of continuous mappings by <i>neu</i>
<a href="http://...very long link"></a>
</t>

<强>解释

  1. 计算全局变量$vTextToSplit 。它是一个字符串,包含三个以管道分隔的值:必须删除的“拆分节点”中的长度,“拆分节点”的generate-id()和“拆分节点”的序号位置在所有文本节点中,按文档顺序。 “拆分节点”是此文本节点,其中包含要生成的文本节点总字符串的最后一个字符。

  2. 将“拆分节点,其”generate-id()及其待修剪长度从“$ vTextToSplit”提取为三个相应的全局变量。

  3. 当文本节点的总长度小于指定的所需长度时,与文档的根(/)匹配的模板会检查边缘情况。如果是这样,则将完整的XML文档复制到输出。如果不是这样,则通过将模板应用于其子节点来继续处理。

  4. 身份规则“按原样”复制所有节点

  5. 匹配任何文本节点的模板会覆盖标识模板。它以三种方式之一处理匹配的文本节点:如果此文本节点的位置小于“拆分节点”,则将其完全复制。如果匹配节点的位置大于“拆分节点”,则不复制其字符串值。最后,如果这是拆分节点本身,则复制其字符串值的所有字符(尾随$vsplitLength字符除外)。

  6. <强> II。 XSLT 2.0解决方案:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes"/>
     <xsl:param name="pMaxLength" select="60"/>
    
     <xsl:template match="node()|@*">
      <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
     </xsl:template>
    
     <xsl:template match=
     "text()[not(sum((.|preceding::text())/string-length(.))
                gt
                 $pMaxLength)
             ]">
      <xsl:copy-of select="."/>
     </xsl:template>
    
     <xsl:template match=
     "text()[sum(preceding::text()/string-length(.))
                gt
                 $pMaxLength
             ]"/>
    
      <xsl:template match=
     "text()[sum((.|preceding::text())/string-length(.))
                ge
                 $pMaxLength
             and
              not(sum(preceding::text()/string-length(.))
                gt
                 $pMaxLength)
             ]">
    
      <xsl:variable name="vprevLength" select=
         "sum(preceding::text()/string-length(.))"/>
    
      <xsl:variable name="vremainingLength" select=
       "$pMaxLength - $vprevLength"/>
      <xsl:copy-of select="substring(.,1,$vremainingLength)"/>
     </xsl:template>
    
    </xsl:stylesheet>
    

    应用于相同的源XML文档(如上所示)时,会产生相同的正确结果

    <t>On the <b>approximate realization</b> of continuous mappings by <i>neu</i><a href="http://...very long link"/></t>
    

    关于性能的说明:对于大型XML文档,两种解决方案都会很慢。避免这种情况的一种方法是使用 scanl() FXSL 功能/模板。当我有更多的空闲时间时,我将在稍后提供第三个解决方案。

答案 2 :(得分:0)

网站设计绝不应限制您可以在容器中放置哪些信息。你可以设置元素的最大高度并使用css在悬停时允许全高度 - 你必须使用一些定位和可能的一些负边距来防止其他元素跳跃。

或者你可以使用text-overflow css属性但是这还没有完全实现(据我所知) - 奇怪的是它在ie6&gt;中被支持了。 !

正则表达式解决方案很难 - 你需要找到带有标签的文本末尾的位置,用标签剪切字符串的剩余部分并附加任何未封闭的标签 - 这是一个棘手的问题!