开发者

Targeting plain text with jQuery

I'm trying to isolate (and then manipulate) quote block formatted in common newsreader and email-clie开发者_Python百科nt manner.

The HTML:

<p>
  Hello there!
  <br />
  I'm great, how are you?
  <br />
  <br />
  Someone wrote:
  <br />
  > Greetings,
  <br />
  > How are you?
</p>

I need to target all the lines that start with >, and hide them as a collapsable block. In the above example everything bellow "Someone wrote:" would then be hidden, saved as a variable and the end result produced by JS would be:

<p>
  Hello there!
  <br />
  I'm great, how are you?
  <br />
  <br />
  Someone wrote:
  <br />
  <a href="#">Click to expand</a>
</p>

Gmail does the same thing, but it serverside wraps the quotation block in a <div>, due to the specific nature of my project the complete process has to be done by JS alone. I'm working with the jQuery framework.

Thanks in advance!


I don't believe jquery parses text like this. You'll have to parse it yourself for lines that start with '>' and edit the string as you'd like. Then you can use jquery to act on the elements you added.


I put together an example for you at this pastebin. Here is the code with comments added.

HTML

<p>
  Hello there!
  <br />
  I'm great, how are you?
  <br />
  <br />
  Someone wrote:
  <br />
  > Greetings,
  <br />
  > How are you?
  <br />
  <br />
  Someone else wrote:
  <br />
  > I like turtles
  <br />
  <br />
  > Someone odd person wrote:
  <br />
  > > You smell like cheese
  <br />
  > > and now I'm hungry
  <br />
  <br />
  and that's the end,
  <br />
  of all of this.
</p>

Script

$(document).ready(function(){
 // link text to inform users to click to expand
 var lnk = '[+]';
 // variable to look for stating it's a new reply
 var newrply = 'wrote:';
 // reply indicator (HTML escape code for ' > ' to exclude any HTML that might be found
 var isrply = '&gt; ';
 // collect html and split it into an array
 var txt = $('p').html().split('<br>');
 // flag showing that the text is within a reply block
 var rply = false;
 // cycle through each portion of text
 $.each(txt, function(i){
  // look for a new reply
  if (this.match(newrply)){
   // if within a reply and it finds a new reply, close previous
   var tmp = (rply) ? '</div>' : '';
   // add link
   txt[i] = tmp + txt[i] + ' <a href="#" class="replylink">' + lnk + '</a>';
   // go to next variable in array and add wrapper, this makes sure the <br> is outside the reply (formatting purposes)
   txt[i+1] = '<div class="reply">' + txt[i+1];
   // look for reply indicator or text that is <5 characters in length
   // (in the HTML above, the array value will have carriage return plus two spaces for each <br>)
  } else if (this.match(isrply) || txt[i].length < 5) {
   rply = true;
  } else {
   rply = false;
   // close the reply, add the close to the previous array element (most likely a <br>)
   txt[i-1] = txt[i-1] + '</div>';
  }
  // close the reply at the end of the array
  if(i == txt.length) {
   txt[i-1] = txt[i-1] + '</div>';
  }
 })
 // join the array and add it back
 $('p').html( txt.join('<br>') );
 // hide the replies
 $('.reply').hide();
 // add toggle view
 $('.replylink').click(function(){
  $(this).next().next('.reply').toggle();
  return false;
 })
})

I changed the link to just a '[+]' to toggle the view but I didn't bother changing it to '[-]' when the reply is open. I figured the code was getting long enough as it is for this example.


With the new code you posted, I had to make a few changes.

  • It will now work with multiple posts (it processes each "div.post")
  • It will now only find a new reply if the ">" is at the beginning of a new line
  • It uses the rel tag to index each reply since the .next() function of jQuery will find "
    " and the number of these was variable
  • One problem I had was with the click function, I ended up switching to .live because the click event was being triggered twice (I couldn't figure out why, but using live works).
  • Lastly, I left the <a name="" style="color: gray;"/> in the code, but that is not properly formatted HTML... you can't close an <a> tag this way.

New Update:

  • Fixed the script to work with IE, apparently IE uses <BR> instead of <br> so the split wasn't working. I ended up using $.browser.msie even though it isn't recommended. Also, the original script left unopened </div> which is why it broke in IE as well.
  • The rply variable I used before wasn't updating between the iterations of the each function, so I moved it's value into a hidden input tag. I tried making it global, but it just wouldn't cooperate. It's probably not the ideal way to do this, so fix/adjust as you desire.

Required HTML

<input id="replyflag" type="hidden" value="false"/>

Updated Code for IE & new pastbin posting:

$(document).ready(function(){
 $('div.post').each(function(){
  // link text to inform users to click to expand
  var lnk = '[+]';
  // variable to look for stating it's a new reply
  var newrply = 'wrote:';
  // reply indicator (HTML escape code for ' > ' to exclude any HTML that might be found
  var isrply = '&gt;';
  // IE capitalizes the <BR>, collect html and split it into an array
  var splt = ($.browser.msie) ? '<BR>' : '<br>';
  var txt = $(this).find('p:eq(0)').html().split(splt);
  // index of each reply in a post
  var indx = 0;
  // start <div> tag around contents, as the script automatically closes the tag, even without replies
  txt[0] = '<div>' + txt[0];
  // cycle through each portion of text
  $.each(txt, function(i){
   // look for a new reply
   if (this.match(newrply)){
    // if within a reply and it finds a new reply, close previous
    var tmp = ($('#replyflag').val()) ? '</div>' : '';
    // set the "within a reply flag" to true
    $('#replyflag').val(true);
    // increment index
    indx++;
    // add link, the rel attrib contains the index of the reply
    txt[i] = tmp + txt[i] + ' <a href="#" class="replylink" rel="' + indx + '">' + lnk + '</a>';
    // go to next variable in array and add wrapper, this makes sure the <br> is outside the reply (formatting purposes)
    txt[i+1] = '<div class="reply" rel="' + indx + '">' + txt[i+1];
   // look for reply indicator at the beginning of a line or text that is > 3 characters in length, if not there, turn off reply flag.
   } else if (this.substring(0,4)!=isrply | this.length > 3) {
    $('#replyflag').val(false);
   }
   // close the reply at the end of the array
   if (i >= txt.length-1) {
    txt[i] = txt[i] + '</div>';
   }
  })
  // join the array and add it back
  $(this).find('p:eq(0)').html( txt.join('<br>') );
  // hide the replies
  $('.reply').hide();
  // add toggle view (using live because sometimes the click event is called twice and the toggle appears to not work)
  $('.replylink').live('click',function(){
   $(this).parent().find('.reply[rel=' + $(this).attr('rel') + ']').toggle();
   return false;
  })
 })
})
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜