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 = '> ';
// 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 = '>';
// 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;
})
})
})
精彩评论