Ограничение длины встроенного элемента - PullRequest
3 голосов
/ 04 августа 2011

Как бы вы ограничили длину текстового элемента переменной длины, который может содержать текстовые атрибуты (<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>...

Имейте в виду, что сами теги (и их атрибуты) не должны учитываться при расчете длины.

Кроме того, текст должен переноситься, поэтому об использовании ширины и переполнения не может быть и речи.

Мистер и Димитр Новатчев имеют отличные решения. Мне больше нравится помещать эту логику в свой xslt, поэтому я выбираю Димитра Новатчева в качестве ответа, хотя обе должны быть.

Ответы [ 3 ]

2 голосов
/ 04 августа 2011

Это преобразование 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.

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 голосов
/ 04 августа 2011

Вот попытка решения:

    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. Регулярное выражение соответствует всем тегам, указанным в вашем сообщении.Вы можете расширить его, чтобы включить больше персонажей в зависимости от вашего реального сценария.Однако анализ HTML с помощью Regex всегда будет сложным.
  2. Если ваша входная строка не содержит сбалансированных тегов (т. Е. Для каждого открывающего тега есть закрывающий тег), это может работать не так, как ожидалось.
  3. Если вы ожидаете самозакрывающиеся теги (например, <br />) или открытые теги input во входной строке, тогда необходима предварительная очистка.Идея та же: получить группу совпадений, пройти через LimitText и заново вставить эти теги в строку результата.
  4. Окончательный отображаемый текст в браузере может все еще не будетудовлетворительно, так как размер шрифта или разрешение экрана могут привести к неверным результатам.Для этого вам нужно прибегнуть к решению на основе JS.

Это должно помочь вам начать, а затем расширить его для любых угловых случаев.

0 голосов
/ 04 августа 2011

дизайн сайта никогда не должен ограничивать какую информацию вы можете разместить в контейнере.Вы можете установить максимальную высоту элемента и использовать css allow full height при наведении курсора - вам придется использовать некоторое позиционирование и, возможно, некоторое отрицательное поле, чтобы другие элементы не прыгали.

В качестве альтернативы вы можете использоватьсвойства css переполнения текста, но это еще не полностью реализовано (насколько я знаю) - как ни странно, его предположительно поддерживается в ie6>!

решение регулярных выражений является сложным - вам нужно найти положение концатекста с удаленными тегами, обрежьте оставшуюся часть строки с тегами и добавьте все открытые теги - хитрый!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...