SitePoint Sponsor

User Tag List

Results 1 to 3 of 3
  1. #1
    SitePoint Zealot Bootfit's Avatar
    Join Date
    Jun 2005
    Location
    Liverpool, UK
    Posts
    149
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Show more/less smooth expanding div

    I have a div on a site that contains a large amount of text. I want to be able to show the div at a certain height (say 200pxls high) with the remainder of the copy hidden until the user clicks the 'read more...' link when the div smoothly expands to show the full copy and the 'Show more...' link is replaced with 'Show less...'.

    I've found script that allows me to do this, but only by making the div jump from one state to the other, rather than a smooth expand.

    Can anyone help with this?

    Cheers.

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,716
    Mentioned
    103 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Bootfit View Post
    Can anyone help with this?
    Can we see what you already have for the page itself?
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Zealot Bootfit's Avatar
    Join Date
    Jun 2005
    Location
    Liverpool, UK
    Posts
    149
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Cheers for helping

    I have the following (and obviously I didn't write this myself but downloaded it from the MIT website, so all credit to Karl Swedberg) - this is an updated version to the version I previously had, this one does scroll/expand but its pretty jerky at the start/finish where the copy disappears/reappears.

    Code:
    /*!
     * jQuery Expander Plugin v1.4.2
     *
     * Date: Sat Mar 31 20:51:48 2012 EDT
     * Requires: jQuery v1.3+
     *
     * Copyright 2011, Karl Swedberg
     * Dual licensed under the MIT and GPL licenses (just like jQuery):
     * http://www.opensource.org/licenses/mit-license.php
     * http://www.gnu.org/licenses/gpl.html
     *
     *
     *
     *
    */
    
    (function($) {
      $.expander = {
        version: '1.4.2',
        defaults: {
          // the number of characters at which the contents will be sliced into two parts.
          slicePoint: 100,
    
          // whether to keep the last word of the summary whole (true) or let it slice in the middle of a word (false)
          preserveWords: true,
    
          // a threshold of sorts for whether to initially hide/collapse part of the element's contents.
          // If after slicing the contents in two there are fewer words in the second part than
          // the value set by widow, we won't bother hiding/collapsing anything.
          widow: 4,
    
          // text displayed in a link instead of the hidden part of the element.
          // clicking this will expand/show the hidden/collapsed text
          expandText: 'read more',
          expandPrefix: '… ',
    
          expandAfterSummary: false,
    
          // class names for summary element and detail element
          summaryClass: 'summary',
          detailClass: 'details',
    
          // class names for <span> around "read-more" link and "read-less" link
          moreClass: 'read-more',
          lessClass: 'read-less',
    
          // number of milliseconds after text has been expanded at which to collapse the text again.
          // when 0, no auto-collapsing
          collapseTimer: 0,
    
          // effects for expanding and collapsing
          expandEffect: 'slideDown',
          expandSpeed: 1000,
          collapseEffect: 'slideUp',
          collapseSpeed: 1000,
    
          // allow the user to re-collapse the expanded text.
          userCollapse: true,
    
          // text to use for the link to re-collapse the text
          userCollapseText: 'read less',
          userCollapsePrefix: ' ',
    
    
          // all callback functions have the this keyword mapped to the element in the jQuery set when .expander() is called
    
          onSlice: null, // function() {}
          beforeExpand: null, // function() {},
          afterExpand: null, // function() {},
          onCollapse: null // function(byUser) {}
        }
      };
    
      $.fn.expander = function(options) {
        var meth = 'init';
    
        if (typeof options == 'string') {
          meth = options;
          options = {};
        }
    
        var opts = $.extend({}, $.expander.defaults, options),
            rSelfClose = /^<(?:area|br|col|embed|hr|img|input|link|meta|param).*>$/i,
            rAmpWordEnd = opts.wordEnd || /(&(?:[^;]+;)?|[a-zA-Z\u00C0-\u0100]+)$/,
            rOpenCloseTag = /<\/?(\w+)[^>]*>/g,
            rOpenTag = /<(\w+)[^>]*>/g,
            rCloseTag = /<\/(\w+)>/g,
            rLastCloseTag = /(<\/[^>]+>)\s*$/,
            rTagPlus = /^<[^>]+>.?/,
            delayedCollapse;
    
        var methods = {
          init: function() {
            this.each(function() {
              var i, l, tmp, newChar, summTagless, summOpens, summCloses,
                  lastCloseTag, detailText, detailTagless,
                  $thisDetails, $readMore,
                  openTagsForDetails = [],
                  closeTagsForsummaryText = [],
                  defined = {},
                  thisEl = this,
                  $this = $(this),
                  $summEl = $([]),
                  o = $.meta ? $.extend({}, opts, $this.data()) : opts,
                  hasDetails = !!$this.find('.' + o.detailClass).length,
                  hasBlocks = !!$this.find('*').filter(function() {
                    var display = $(this).css('display');
                    return (/^block|table|list/).test(display);
                  }).length,
                  el = hasBlocks ? 'div' : 'span',
                  detailSelector = el + '.' + o.detailClass,
                  moreSelector = 'span.' + o.moreClass,
                  expandSpeed = o.expandSpeed || 0,
                  allHtml = $.trim( $this.html() ),
                  allText = $.trim( $this.text() ),
                  summaryText = allHtml.slice(0, o.slicePoint);
    
              // bail out if we've already set up the expander on this element
              if ( $.data(this, 'expander') ) {
                return;
              }
    
              $.data(this, 'expander', true);
    
              // determine which callback functions are defined
              $.each(['onSlice','beforeExpand', 'afterExpand', 'onCollapse'], function(index, val) {
                defined[val] = $.isFunction(o[val]);
              });
    
              // back up if we're in the middle of a tag or word
              summaryText = backup(summaryText);
    
              // summary text sans tags length
              summTagless = summaryText.replace(rOpenCloseTag, '').length;
    
              // add more characters to the summary, one for each character in the tags
              while (summTagless < o.slicePoint) {
                newChar = allHtml.charAt(summaryText.length);
                if (newChar == '<') {
                  newChar = allHtml.slice(summaryText.length).match(rTagPlus)[0];
                }
                summaryText += newChar;
                summTagless++;
              }
    
              summaryText = backup(summaryText, o.preserveWords);
    
              // separate open tags from close tags and clean up the lists
              summOpens = summaryText.match(rOpenTag) || [];
              summCloses = summaryText.match(rCloseTag) || [];
    
              // filter out self-closing tags
              tmp = [];
              $.each(summOpens, function(index, val) {
                if ( !rSelfClose.test(val) ) {
                  tmp.push(val);
                }
              });
              summOpens = tmp;
    
              // strip close tags to just the tag name
              l = summCloses.length;
              for (i = 0; i < l; i++) {
                summCloses[i] = summCloses[i].replace(rCloseTag, '$1');
              }
    
              // tags that start in summary and end in detail need:
              // a). close tag at end of summary
              // b). open tag at beginning of detail
              $.each(summOpens, function(index, val) {
                var thisTagName = val.replace(rOpenTag, '$1');
                var closePosition = $.inArray(thisTagName, summCloses);
                if (closePosition === -1) {
                  openTagsForDetails.push(val);
                  closeTagsForsummaryText.push('</' + thisTagName + '>');
    
                } else {
                  summCloses.splice(closePosition, 1);
                }
              });
    
              // reverse the order of the close tags for the summary so they line up right
              closeTagsForsummaryText.reverse();
    
              // create necessary summary and detail elements if they don't already exist
              if ( !hasDetails ) {
    
                // end script if there is no detail text or if detail has fewer words than widow option
                detailText = allHtml.slice(summaryText.length);
                detailTagless = $.trim( detailText.replace(rOpenCloseTag, '') );
    
                if ( detailTagless === '' || detailTagless.split(/\s+/).length < o.widow ) {
                  return;
                }
                // otherwise, continue...
                lastCloseTag = closeTagsForsummaryText.pop() || '';
                summaryText += closeTagsForsummaryText.join('');
                detailText = openTagsForDetails.join('') + detailText;
    
              } else {
                // assume that even if there are details, we still need readMore/readLess/summary elements
                // (we already bailed out earlier when readMore el was found)
                // but we need to create els differently
    
                // remove the detail from the rest of the content
                detailText = $this.find(detailSelector).remove().html();
    
                // The summary is what's left
                summaryText = $this.html();
    
                // allHtml is the summary and detail combined (this is needed when content has block-level elements)
                allHtml = summaryText + detailText;
    
                lastCloseTag = '';
              }
              o.moreLabel = $this.find(moreSelector).length ? '' : buildMoreLabel(o);
    
              if (hasBlocks) {
                detailText = allHtml;
              }
              summaryText += lastCloseTag;
    
              // onSlice callback
              o.summary = summaryText;
              o.details = detailText;
              o.lastCloseTag = lastCloseTag;
    
              if (defined.onSlice) {
                // user can choose to return a modified options object
                // one last chance for user to change the options. sneaky, huh?
                // but could be tricky so use at your own risk.
                tmp = o.onSlice.call(thisEl, o);
    
              // so, if the returned value from the onSlice function is an object with a details property, we'll use that!
                o = tmp && tmp.details ? tmp : o;
              }
    
              // build the html with summary and detail and use it to replace old contents
              var html = buildHTML(o, hasBlocks);
    
              $this.html( html );
    
              // set up details and summary for expanding/collapsing
              $thisDetails = $this.find(detailSelector);
              $readMore = $this.find(moreSelector);
              $thisDetails.hide();
              $readMore.find('a').unbind('click.expander').bind('click.expander', expand);
    
              $summEl = $this.find('div.' + o.summaryClass);
    
              if ( o.userCollapse && !$this.find('span.' + o.lessClass).length ) {
                $this
                .find(detailSelector)
                .append('<span class="' + o.lessClass + '">' + o.userCollapsePrefix + '<a href="#">' + o.userCollapseText + '</a></span>');
              }
    
              $this
              .find('span.' + o.lessClass + ' a')
              .unbind('click.expander')
              .bind('click.expander', function(event) {
                event.preventDefault();
                clearTimeout(delayedCollapse);
                var $detailsCollapsed = $(this).closest(detailSelector);
                reCollapse(o, $detailsCollapsed);
                if (defined.onCollapse) {
                  o.onCollapse.call(thisEl, true);
                }
              });
    
              function expand(event) {
                event.preventDefault();
                $readMore.hide();
                $summEl.hide();
                if (defined.beforeExpand) {
                  o.beforeExpand.call(thisEl);
                }
    
                $thisDetails.stop(false, true)[o.expandEffect](expandSpeed, function() {
                  $thisDetails.css({zoom: ''});
                  if (defined.afterExpand) {o.afterExpand.call(thisEl);}
                  delayCollapse(o, $thisDetails, thisEl);
                });
              }
    
            }); // this.each
          },
          destroy: function() {
            if ( !this.data('expander') ) {
              return;
            }
            this.removeData('expander');
            this.each(function() {
              var $this = $(this),
                  o = $.meta ? $.extend({}, opts, $this.data()) : opts,
                  details = $this.find('.' + o.detailClass).contents();
    
              $this.find('.' + o.moreClass).remove();
              $this.find('.' + o.summaryClass).remove();
              $this.find('.' + o.detailClass).after(details).remove();
              $this.find('.' + o.lessClass).remove();
    
            });
          }
        };
    
        // run the methods (almost always "init")
        if ( methods[meth] ) {
          methods[ meth ].call(this);
        }
    
        // utility functions
        function buildHTML(o, blocks) {
          var el = 'span',
              summary = o.summary;
          if ( blocks ) {
            el = 'div';
            // if summary ends with a close tag, tuck the moreLabel inside it
            if ( rLastCloseTag.test(summary) && !o.expandAfterSummary) {
              summary = summary.replace(rLastCloseTag, o.moreLabel + '$1');
            } else {
            // otherwise (e.g. if ends with self-closing tag) just add moreLabel after summary
            // fixes #19
              summary += o.moreLabel;
            }
    
            // and wrap it in a div
            summary = '<div class="' + o.summaryClass + '">' + summary + '</div>';
          } else {
            summary += o.moreLabel;
          }
    
          return [
            summary,
            '<',
              el + ' class="' + o.detailClass + '"',
            '>',
              o.details,
            '</' + el + '>'
            ].join('');
        }
    
        function buildMoreLabel(o) {
          var ret = '<span class="' + o.moreClass + '">' + o.expandPrefix;
          ret += '<a href="#">' + o.expandText + '</a></span>';
          return ret;
        }
    
        function backup(txt, preserveWords) {
          if ( txt.lastIndexOf('<') > txt.lastIndexOf('>') ) {
            txt = txt.slice( 0, txt.lastIndexOf('<') );
          }
          if (preserveWords) {
            txt = txt.replace(rAmpWordEnd,'');
          }
    
          return $.trim(txt);
        }
    
        function reCollapse(o, el) {
          el.stop(true, true)[o.collapseEffect](o.collapseSpeed, function() {
            var prevMore = el.prev('span.' + o.moreClass).show();
            if (!prevMore.length) {
              el.parent().children('div.' + o.summaryClass).show()
                .find('span.' + o.moreClass).show();
            }
          });
        }
    
        function delayCollapse(option, $collapseEl, thisEl) {
          if (option.collapseTimer) {
            delayedCollapse = setTimeout(function() {
              reCollapse(option, $collapseEl);
              if ( $.isFunction(option.onCollapse) ) {
                option.onCollapse.call(thisEl, false);
              }
            }, option.collapseTimer);
          }
        }
    
        return this;
      };
    
      // plugin defaults
      $.fn.expander.defaults = $.expander.defaults;
    })(jQuery);
    
    
    
    
    $(document).ready(function() {
      var opts = {collapseTimer: 4000};
    
      $.each(['beforeExpand', 'afterExpand', 'onCollapse'], function(i, callback) {
        opts[callback] = function(byUser) {
          var by, msg = '<div class="success">' + callback;
    
          if (callback == 'onCollapse') {
            msg += ' (' + (byUser ? 'user' : 'timer') + ')';
          }
          msg += '</div>';
    
          $(this).parent().parent().append(msg)
        }
      });
    
      $('dl.expander dd').eq(0).expander();
      $('dl.expander dd').slice(1).expander(opts);
    
    
      $('ul.expander li').expander({
        slicePoint: 50,
        widow: 2,
        expandSpeed: 0,
        userCollapseText: '[^]'
      });
    
      $('div.expander').expander();
    });



    HTML Code:
    <div id="main">
      <div class="expander" style="background-color:#CCCCCC; border:#999999 1px solid; padding:10px;">
        <p>Paragraph 1: Lorem ipsum dolor sit amet, consectetur adipisicing elit,
          sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
          ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
          ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
          velit esse cillum dolore eu fugiat nulla pariatur. </p>
        <p>Paragraph 2: Excepteur sint occaecat cupidatat non proident, sunt in culpa
          qui officia deserunt mollit anim id est laborum.</p>
      </div>
    </div>


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •