jQuery Plugin "readmore", trim text without cutting words - javascript

I'm using http://rockycode.com/blog/jquery-plugin-readmore/ for trim long text and add a "See more" link to reveal all the text.
I would love to avoid cutting words, how could I do that?
If the limit is 35, don't cut the w...
but
If the limit is 35, don't cut the word... (and in this case, trim it at 38 and then show the hidden text from 39th chtill the end.

Instead of doing this:
$elem.readmore({
substr_len: 35
});
You could do this
$elem.readmore({
substr_len: $elem.text().substr(0, 35).lastIndexOf(" ")
});
What we're doing is to go to the latest space posible before index 35.
Of course 35 can be variable. Also you could put it into a function to reuse it.
Hope this helps

You can change the abridge function within that plugin as follows:
function abridge(elem) {
var opts = elem.data("opts");
var txt = elem.html();
var len = opts.substr_len;
var dots = "<span>" + opts.ellipses + "</span>";
var charAtLen = txt.substr(len, 1);
while (len < txt.length && !/\s/.test(charAtLen)) {
len++;
charAtLen = txt.substr(len, 1);
}
var shown = txt.substring(0, len) + dots;
var hidden = '<span class="hidden" style="display:none;">' + txt.substring(len, txt.length) + '</span>';
elem.html(shown + hidden);
}
...and it will behave as you desire. You might want to add an option to turn this feature off and on, but I'll leave that up to you.
See working example โ†’

I was just gathering information about this subject, with your help and the help from other related posts I wrote this:
http://jsfiddle.net/KHd6J/526/

Related

How to create an automatically word wrap each 40 Characters with JavaScript / JQuery

Is it possible to create an automatically word wrap every 40 insert characters with java script? I have programmed a jQuery / javascript solution, which used the keyUp event of a textfield:
jQuery(textInputfield).keyup(function(event) {
var text = jQuery(textInputfield).val();
if(text.length == 40){
text = text + '<br />';
}
var textOutput = jQuery(textOutputField);
textOutput.empty();
textOutput.append(text);
});
I need a more flexible solution for this code:
if(text.length == 40){
text = text + '<br />';
}
How can i solve this problem?
A regex solution:
var text = jQuery(this).val();
text = text.replace(/.{40}/g, '$&<br/>');
I would suggest against reformatting in this way and instead using a specified width and let the browser make the line break.
Use a specified width and height: auto
EDIT: see this fiddle for example http://jsfiddle.net/bf9rJ/

Collapsable div, but from a certain point defined in a variable

E.g. I have a div that contains all the text like:
<div class="text-container">
<p>
Though we welcome the idea of a smaller connector, we're miffed that Apple couldn't just adopt the semi-industry standard of Micro-USB. That would make things easier for smartphone users across the globe. Yet, even so, the smaller connector may be a smart move for the future. The 30-pin connector has been around since 2003, long before the iPhone even existed: frankly, it's a dust magnet. A smaller connector helps shave extra space to achieve a smaller phone with perhaps a bigger battery. The new connector cable will mainly be used for syncing and charging by most people who own an Apple TV or Bluetooth/AirPlay accessories.
</p>
</div>
And I would like to create something like this:
<div class="text-container">
<p>
Though we welcome the idea of a smaller connector...[show more]
</p>
</div>
I guess I should get all the content of the div and then Find the first e.g. 50 character and put there a link and all the other text put in some div which will be hidden, and after the click on the link the other stuff show up.
It should be toggle-like and change the text from [show more] to [show less] if it is expanded and vice versa.
Any advice how to achieve this with plain javascript and jquery itself and without other jQuery plugins?
Here is another solution.
It doesn't simply cut the words in the middle but checks endings, punctuation, and long words.
$(".text-container p").each(function() {
var val = $.trim(this.innerHTML),
parsed = val.split(/\s+/),
cut = parsed;
// for each word
for (var i = 0, k = 0; i < parsed.length; i++) {
k += parsed[i].length + 1;
if (k - 1 > 50) {
cut = parsed.slice(0, i);
break;
}
}
// if one long word
if (cut.length == 0) {
cut.push(parsed[0].substring(0, 50));
}
val = cut.join(" ");
// if the text is long enough to cut
if (cut.length != parsed.length) {
this.innerHTML = val.replace(/[.,;?!]$/, "")
+ "<span>...</span> ";
$("<span />")
.css("display", "none")
.html(parsed.slice(cut.length).join(" ") + " ")
.appendTo(this);
$("<a />", {
href : "#",
text : "[show more]"
}).on("click", function(e) {
var sh = this.innerHTML == "[show more]";
$(this).prev("span").toggle(sh).prev("span").toggle(!sh);
this.innerHTML = sh ? "[show less]" : "[show more]";
e.preventDefault();
}).appendTo(this);
} else {
this.innerHTML = val;
}
});
DEMO: http://jsfiddle.net/xRuch/
Build a quick demo for you
jQuery
$(function(){
var $el = $(".text-container").find("p");
var str = $el.text();
var str1 = str.substr(0,50), str2= str.substr(51);
$el.html(str1+" <span class='showMore'><a href='#'>Show more...</a></span><span class='moreText'>"+str2+"</span><span class='showLess'><a href='#'>Show Less</a></span>");
$(".showMore").on("click", function(){
$(this).next(".moreText").show();
$(this).next(".moreText").next(".showLess").show();
$(this).hide();
});
$(".showLess").on("click", function(){
$(this).prev(".moreText").hide();
$(this).prev(".moreText").prev(".showMore").show();
$(this).hide();
})
});
css
.moreText, .showLess { display:none};
on modern browsers you could use the css property text-overflow:ellipsis;
http://jsfiddle.net/Y8skB/1/
i used css in my example to expand the text, but you could also do it with jquery if you want a show more link.

JavaScript RegExp match text ignoring HTML

Is it possible to match "the dog is really really fat" in "The <strong>dog</strong> is really <em>really</em> fat!" and add "<span class="highlight">WHAT WAS MATCHED</span>" around it?
I don't mean this specifically, but generally be able to search text ignoring HTML, keeping it in the end result, and just add the span above around it all?
EDIT:
Considering the HTML tag overlapping problem, would it be possible to match a phrase and just add the span around each of the matched words? The problem here is that I don't want the word "dog" matched when it's not in the searched context, in this case, "the dog is really really fat."
Update:
Here is a working fiddle that does what you want. However, you will need to update the htmlTagRegEx to handle matching on any HTML tag, as this just performs a simple match and will not handle all the cases.
http://jsfiddle.net/briguy37/JyL4J/
Also, below is the code. Basically, it takes out the html elements one by one, then does a replace in the text to add the highlight span around the matched selection, and then pushes back in the html elements one by one. It's ugly, but it's the easiest way I could think of to get it to work...
function highlightInElement(elementId, text){
var elementHtml = document.getElementById(elementId).innerHTML;
var tags = [];
var tagLocations= [];
var htmlTagRegEx = /<{1}\/{0,1}\w+>{1}/;
//Strip the tags from the elementHtml and keep track of them
var htmlTag;
while(htmlTag = elementHtml.match(htmlTagRegEx)){
tagLocations[tagLocations.length] = elementHtml.search(htmlTagRegEx);
tags[tags.length] = htmlTag;
elementHtml = elementHtml.replace(htmlTag, '');
}
//Search for the text in the stripped html
var textLocation = elementHtml.search(text);
if(textLocation){
//Add the highlight
var highlightHTMLStart = '<span class="highlight">';
var highlightHTMLEnd = '</span>';
elementHtml = elementHtml.replace(text, highlightHTMLStart + text + highlightHTMLEnd);
//plug back in the HTML tags
var textEndLocation = textLocation + text.length;
for(i=tagLocations.length-1; i>=0; i--){
var location = tagLocations[i];
if(location > textEndLocation){
location += highlightHTMLStart.length + highlightHTMLEnd.length;
} else if(location > textLocation){
location += highlightHTMLStart.length;
}
elementHtml = elementHtml.substring(0,location) + tags[i] + elementHtml.substring(location);
}
}
//Update the innerHTML of the element
document.getElementById(elementId).innerHTML = elementHtml;
}
Naah... just use the good old RegExp ;)
var htmlString = "The <strong>dog</strong> is really <em>really</em> fat!";
var regexp = /<\/?\w+((\s+\w+(\s*=\s*(?:\".*?"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/gi;
var result = '<span class="highlight">' + htmlString.replace(regexp, '') + '</span>';
A simpler way with JQuery would be.
originalHtml = $("#div").html();
newHtml = originalHtml.replace(new RegExp(keyword + "(?![^<>]*>)", "g"), function(e){
return "<span class='highlight'>" + e + "</span>";
});
$("#div").html(newHtml);
This works just fine for me.
Here is a working regex example to exclude matches inside html tags as well as javascripts:
http://refiddle.com/lwy6
Use this regex in a replace() script.
/(a)(?!([^<])*?>)(?!<script[^>]*?>)(?![^<]*?<\/script>|$)/gi
this.keywords.forEach(keyword => {
el.innerHTML = el.innerHTML.replace(
RegExp(keyword + '(?![^<>]*>)', 'ig'),
matched => `<span class=highlight>${matched}</span>`
)
})
You can use string replace with this expression </?\w*> and you'll get your string
If you use jQuery, you can use the text property on the element containing the text you're searching for. Given this markup:
<p id="the-text">
The <strong>dog</strong> is really <em>really</em> fat!
</p>
This would yield "The dog is really really fat!":
$('#the-text').text();
You could do your regex search on that text instead of trying to do so in the markup.
Without jQuery, I'm unsure of an easy way to extract and concatenate the text nodes from all child elements.

Need to find more efficient solution to writing blank spaces to page

I am putting together a page displaying a user's tweets whilst visualising the amount of time in between in the way of blank spaces. Three blank space represent one second of inactivity between tweets. Confusing, I know. You can see from my code โ€“ http://jsfiddle.net/k5234/2/ โ€“ that I have written the individual s into a for loop, but this is proving to be a very inefficient way of programming the page (as you can tell from the load speed!)
Does anyone have any suggestions as to how I can structure the JS better to make page loads quicker whilst keeping the same functionality.
Thanks,
Dalogi
Sounds like a job for css padding/margins. No looping needed, just figure out how many px/ems it is for each second and you have a gap.
Eric
Instead of blank spaces, use CSS margin/padding/whatever to space the items and calculate the relevant values based on the time between tweets. No more page bloat with s
The best way would be to use CSS to pad-out the items. Just use tweetGap as a multiplier and use it to generate the number of pixels padding your require.
If, for some weird reason, you can do this and most of your cases are, say, less than 20 spaces then it would probably be more efficient to build an array and "index" into it using the tweetGap variable. Something like:
var s=new Array();
s[0]="";
s[1]=" ";
s[2]=" ";
s[3]=" ";
s[4]=" ";
// etc
if (tweetGap < 20) {
html+= s[tweetGap];
}
else {
// forloop
}
Horrible code, but probably faster than a loop. But really CSS is the way to go.
I agree with the other answers, but if you do need to use spaces:
function spaces(count) {
var return_ = '';
for(var i=0;i<count+1;i++) return_ += " ";
return return_;
}
document.write(spaces(10)); // ten spaces...
But really, padding/margin is the way to go:
/* CSS */
.spaces.five { margin-left: 3px; }
.spaces.ten { margin-left: 6px; }
[...]
<!-- HTML -->
blablablabla<span class='spaces five' />blablalabla
This is a solution I created that works instantly.
http://jsfiddle.net/k5234/4/
$(document).ready(function(){
$('#target').submit(function() {
var html = "";
var url = "http://api.twitter.com/1/statuses/user_timeline.json?&callback=?&screen_name=dalogi&count=5";
$.getJSON(url, function(data) {
// Retrieving tweets
function getTweets() {
$.each(data, function(i, item) {
var timeCreated = new Date(item.created_at); // date of tweet
if ( (i+1) in data) {
var timeTillNextTweet = new Date( data[ i+1 ].created_at);
var timeDiff = timeCreated.getTime() - timeTillNextTweet.getTime();
var secondsDifference = Math.floor(timeDiff/1000);
var tweetGap = secondsDifference * 3; // Based on 3 characters per second
html += "<p style=\"margin-bottom: " + tweetGap + "em;\">" + item.text + "</p>";
} else {
html += "<p>" + item.text + "</p>";
};
});
}
getTweets();
$('#userTweets').append(html);
});
return false
});
});
how about this
String.prototype.times = function(n) {
return Array.prototype.join.call({length:n+1}, this);
};
"&nbsp".times(5);
WORKING DEMO

Clean Microsoft Word Pasted Text using JavaScript

I am using a 'contenteditable' <div/> and enabling PASTE.
It is amazing the amount of markup code that gets pasted in from a clipboard copy from Microsoft Word. I am battling this, and have gotten about 1/2 way there using Prototypes' stripTags() function (which unfortunately does not seem to enable me to keep some tags).
However, even after that, I wind up with a mind-blowing amount of unneeded markup code.
So my question is, is there some function (using JavaScript), or approach I can use that will clean up the majority of this unneeded markup?
Here is the function I wound up writing that does the job fairly well (as far as I can tell anyway).
I am certainly open for improvement suggestions if anyone has any. Thanks.
function cleanWordPaste( in_word_text ) {
var tmp = document.createElement("DIV");
tmp.innerHTML = in_word_text;
var newString = tmp.textContent||tmp.innerText;
// this next piece converts line breaks into break tags
// and removes the seemingly endless crap code
newString = newString.replace(/\n\n/g, "<br />").replace(/.*<!--.*-->/g,"");
// this next piece removes any break tags (up to 10) at beginning
for ( i=0; i<10; i++ ) {
if ( newString.substr(0,6)=="<br />" ) {
newString = newString.replace("<br />", "");
}
}
return newString;
}
Hope this is helpful to some of you.
You can either use the full CKEditor which cleans on paste, or look at the source.
I am using this:
$(body_doc).find('body').bind('paste',function(e){
var rte = $(this);
_activeRTEData = $(rte).html();
beginLen = $.trim($(rte).html()).length;
setTimeout(function(){
var text = $(rte).html();
var newLen = $.trim(text).length;
//identify the first char that changed to determine caret location
caret = 0;
for(i=0;i < newLen; i++){
if(_activeRTEData[i] != text[i]){
caret = i-1;
break;
}
}
var origText = text.slice(0,caret);
var newText = text.slice(caret, newLen - beginLen + caret + 4);
var tailText = text.slice(newLen - beginLen + caret + 4, newLen);
var newText = newText.replace(/(.*(?:endif-->))|([ ]?<[^>]*>[ ]?)|( )|([^}]*})/g,'');
newText = newText.replace(/[ยท]/g,'');
$(rte).html(origText + newText + tailText);
$(rte).contents().last().focus();
},100);
});
body_doc is the editable iframe, if you are using an editable div you could drop out the .find('body') part. Basically it detects a paste event, checks the location cleans the new text and then places the cleaned text back where it was pasted. (Sounds confusing... but it's not really as bad as it sounds.
The setTimeout is needed because you can't grab the text until it is actually pasted into the element, paste events fire as soon as the paste begins.
How about having a "paste as plain text" button which displays a <textarea>, allowing the user to paste the text in there? that way, all tags will be stripped for you. That's what I do with my CMS; I gave up trying to clean up Word's mess.
You can do it with regex
Remove head tag
Remove script tags
Remove styles tag
let clipboardData = event.clipboardData || window.clipboardData;
let pastedText = clipboardData.getData('text/html');
pastedText = pastedText.replace(/\<head[^>]*\>([^]*)\<\/head/g, '');
pastedText = pastedText.replace(/\<script[^>]*\>([^]*)\<\/script/g, '');
pastedText = pastedText.replace(/\<style[^>]*\>([^]*)\<\/style/g, '');
// pastedText = pastedText.replace(/<(?!(\/\s*)?(b|i|u)[>,\s])([^>])*>/g, '');
here the sample : https://stackblitz.com/edit/angular-u9vprc
I did something like that long ago, where i totally cleaned up the stuff in a rich text editor and converted font tags to styles, brs to p's, etc, to keep it consistant between browsers and prevent certain ugly things from getting in via paste. I took my recursive function and ripped out most of it except for the core logic, this might be a good starting point ("result" is an object that accumulates the result, which probably takes a second pass to convert to a string), if that is what you need:
var cleanDom = function(result, n) {
var nn = n.nodeName;
if(nn=="#text") {
var text = n.nodeValue;
}
else {
if(nn=="A" && n.href)
...;
else if(nn=="IMG" & n.src) {
....
}
else if(nn=="DIV") {
if(n.className=="indent")
...
}
else if(nn=="FONT") {
}
else if(nn=="BR") {
}
if(!UNSUPPORTED_ELEMENTS[nn]) {
if(n.childNodes.length > 0)
for(var i=0; i<n.childNodes.length; i++)
cleanDom(result, n.childNodes[i]);
}
}
}
This works great to remove any comments from HTML text, including those from Word:
function CleanWordPastedHTML(sTextHTML) {
var sStartComment = "<!--", sEndComment = "-->";
while (true) {
var iStart = sTextHTML.indexOf(sStartComment);
if (iStart == -1) break;
var iEnd = sTextHTML.indexOf(sEndComment, iStart);
if (iEnd == -1) break;
sTextHTML = sTextHTML.substring(0, iStart) + sTextHTML.substring(iEnd + sEndComment.length);
}
return sTextHTML;
}
Had a similar issue with line-breaks being counted as characters and I had to remove them.
$(document).ready(function(){
$(".section-overview textarea").bind({
paste : function(){
setTimeout(function(){
//textarea
var text = $(".section-overview textarea").val();
// look for any "\n" occurences and replace them
var newString = text.replace(/\n/g, '');
// print new string
$(".section-overview textarea").val(newString);
},100);
}
});
});
Could you paste to a hidden textarea, copy from same textarea, and paste to your target?
Hate to say it, but I eventually gave up making TinyMCE handle Word crap the way I want. Now I just have an email sent to me every time a user's input contains certain HTML (look for <span lang="en-US"> for example) and I correct it manually.

Categories