JSFiddle - React, Tailwind, and code Playground

by jeremyblalock

HTML

<div class='wrapper'>
<p>The board, which Congress made an independent agency in 2007 and became fully operational around the time that Edward J. Snowden began releasing a trove of N.S.A. documents, concluded that the agency largely abided by the rules set out by Congress as it gathered the communications of foreigners, a process that necessarily swept in some emails and phone calls involving American citizens.

The report was issued just as Congress is considering changes in the laws governing N.S.A. activities. But the legislation, which has passed the House and is under consideration by the Senate, deals largely with the call-records program, which both the board and President Obama said in January must be changed. That program involved the agency’s retention of billions of records for all phone calls made from or to the United States; under the proposed legislation, telecommunications companies would retain those records, and the N.S.A. would have access under court orders.

The privacy board found that program illegal, and said it should be terminated. But the most recent report, adopted by the board on Wednesday, deals with what the agency calls “702 collection,” a reference to Section 702 of the Federal Intelligence Surveillance Act, which was amended in 2008 after The New York Times revealed a program of warrantless wiretapping that the Bush administration started after the Sept. 11, 2001, attacks.

</p>
</div>

CSS

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}
body {
    line-height: 1.25em;
}
.wrapper {
    margin: 60px;
}

JavaScript

(function($) {
  function mutableClamp($el, lines, moreText, lessText) {
    var text = $el.text(),
        chars = text.split(''),
        $clampTop = $('<span class="clamp-visible"></span>'),
        $clampTopText = $('<span></span>'),
        $clampBottom = $('<span class="clamp-hidden"></span>')
                          .text(text + ' ').hide(),
        $clampEllipsis = $('<span>... </span>'),
        $clampLink = $('<a class="clamp-link" href="#"></a>')
                          .text(moreText),
        $hideLink = $('<a class="clamp-link" href="#"></a>').text(lessText),
        lineHeight = parseInt($el.css('line-height')) + 1,
        maxHeight = lineHeight * lines,
        nextChar;
      
        console.log('============>', maxHeight);

    if ($el.height() < maxHeight) {
      return;
    }

    $el.text('');
    $clampBottom.append($hideLink);
    $clampTop.append($clampTopText, $clampEllipsis, $clampLink);
    $el.append($clampTop, $clampBottom);

    while ($clampTop.height() < maxHeight) {
      nextChar = chars.shift();
      if (typeof nextChar === 'undefined') {
        $el.text(text);
        return;
      }
      $clampTopText.text($clampTopText.text() + nextChar);
    }
    $clampTopText.text($clampTopText.text().slice(0, -1));

    $clampLink.on('click', function(e) {
      e.preventDefault();
      $clampTop.hide();
      $clampBottom.show();
    });
    $hideLink.on('click', function(e) {
      e.preventDefault();
      $clampTop.show();
      $clampBottom.hide();
    });
  }

  $.fn.clamp = function(lines, moreText, lessText) {
    var $el = this;
    if (typeof moreText === 'undefined') {
      moreText = 'show more';
    }
    if (typeof lessText === 'undefined') {
      lessText = lessText || 'show less';
    }
    $el.each(function() {
      mutableClamp($(this), lines, moreText, lessText);
    });
    return $el;
  };
})($);

$('p').clamp(3);