jQuery ellipsis based on available space - javascript

I want to be able to ellipsis text based on how much space is available. Currently I have to provide the maximum number of characters I want to my ellipsis function but it would be far better if it did it based on available space.
How can I achieve this?
function ellipsisText(object, maxLength, ellipsistooltip) {
var grace = 3;
var text = object.text();
if (text.length - grace > maxLength) {
var etext = text.substring(0, maxLength);
etext += "...";
object.text(etext);
if (ellipsistooltip) {
object.addClass("tooltip");
object.attr("tooltiptitle", text);
}
}
}

Related

Get not overflowing text length dynamically

Is there a way to get the length of visible part of an overflown text (or the size of the overflown part, to calculate the rest, for the case) with CSS or JavaScript?
And if so, could it be calculated dynamically (i.e. on window resize?)
The idea is to make a read more button which span button always sticks at the end of the last visible line, it doesn't matter the screen size.
Something like this involves a lot of factors, for instance, your "text" could be a mixture of text and other elements (<b>, <i>, <img>). Assuming it's just straight text, the following works by splitting the string at various halves, with successively smaller halves, to eventually arrive at the text that gives the same height as your source element with hidden overflow.
function getVisibleText(source)
{
let yardstick = document.createElement(source.nodeName);
let sourceRectangle = source.getBoundingClientRect();
let text = source.textContent;
yardstick.style.width = sourceRectangle.width + "px";
yardstick.style.position = "absolute";
yardstick.style.top = "-999px";
yardstick.style.left = "-999px";
yardstick.textContent = text;
source.parentNode.appendChild(yardstick);
let size = text.length;
let difference = size;
let yardstickRectangle = yardstick.getBoundingClientRect();
let result = text;
while((difference > 1 || yardstickRectangle.height > sourceRectangle.height) && size > 0)
{
difference = Math.round(difference / 2);
if(yardstickRectangle.height > sourceRectangle.height)
size -= difference;
else
size += difference;
result = text.substring(0, size);
yardstick.textContent = result;
yardstickRectangle = yardstick.getBoundingClientRect();
}
yardstick.parentNode.removeChild(yardstick);
// Trim to the last whole word
let match = (new RegExp("\\s+\\S*?$", "g")).exec(result)[0];
if(match)
result = result.substring(0, result.length - match.length);
return(result);
}

Limit lines and characters per line in textarea (Javascript, jQuery)

What I need is to be able to limit the number of lines in a textarea. And to limit the number of characters in each line (force adding a newline when maximum number of characters has been added.
I am not interested in the "rows" and "cols" attributes. They do not work.
Also, I would like to have it working even if the user cuts or pastes something, or if he returns to a line and modifies it.
Not sure if this is an overly complex way of doing it but you can loop through the textarea and add a newline every X characters. This solution won't allow users to insert their own line breaks (it strips off any existing line breaks) .
<textarea onkeyup="formatTextArea(this)"></textarea>
<script type="text/javascript">
function formatTextArea(myArea)
{
//strip off any line breaks first
var str = myArea.value.replace(/\n|\r/g, "");
var result = '';
var i = 0
var formattedText = '';
//number of lines needed
var limit = 5
// number of characters
var limitPerLine = 20;
// loop through the text, adding a new line every limitPerLine characters
// stop after limit lines
while (str.length > 0 && i < limit)
{
i++;
formattedText += str.substring(0, limitPerLine);
str = str.substring(limitPerLine);
//only add a new line if we're not at the end of the content
if(str.length > 0 && i < limit)
{
formattedText += '\n';
}
}
myArea.value = formattedText;
}
</script>

How to move cursor to next/previous word in textarea?

How would you move the cursor to the next or previous word in a textarea using Javascript? I'm trying to replicate the Emacs commands "forward one word" and "back one word" in an HTML textarea.
I can get the current caret/cursor position using rangyinputs, but I'm not yet sure how to efficiently move to the next word without using various splits that could be slow on very long pieces of text.
I used setCaretToTextEnd() from here and .selectRange() from here. The following functions use Emacs style caret positions, and is more efficient than looping through words.
function nextWord(input) {
let currentCaretPosition = input.selectionStart;
// -1 Because Emacs goes to end of next word.
let nextWordPosition = input.value.indexOf(' ', currentCaretPosition) - 1;
if (nextWordPosition < 0) {
input.setCaretToTextEnd();
} else {
input.selectRange(nextWordPosition);
}
}
function previousWord(input) {
let currentCaretPosition = input.selectionStart;
// +1 Because Emacs goes to start of previous word.
let previousWordPosition = input.value.lastIndexOf(' ', currentCaretPosition) + 1;
if (previousWordPosition < 0) {
input.selectRange(0);
} else {
input.selectRange(previousWordPosition);
}
}
See this fiddle. I used functions from jQuery Set Cursor Position in Text Area to change position of cursor.
function nextWord(input) {
var words = input.value.split(" "),
index = 0;
for (var i in words) {
var word = words[i];
if (index+word.length >= input.selectionStart) {
setCaretToPos(input, index+word.length+1);
break;
}
index += word.length+1;
}
}
function previousWord(input) {
var words = input.value.split(" ").reverse(),
index = input.value.length;
for (var i in words) {
var word = words[i];
if (index+1 <= input.selectionStart) {
setCaretToPos(input, index-word.length);
break;
}
index -= word.length+1;
}
}

Remove excess words from a textbox

I have a script which is almost complete but I can't figure out the last bit here. The script is meant to limit the amount of words that can be entered into a text area and if they go over the word limit these extra words are removed. I have the amount of words beyond the max labeled as overage. For instance, if you were to enter in 102 words, then the overage would be 2. How would I remove those two words from the text area?
jQuery(document).ready(function($) {
var max = 100;
$('#text').keyup(function(e) {
if (e.which < 0x20) {
return;
}
var value = $('#text').val();
var regex = /\s+/gi;
var wordCount = value.trim().replace(regex, ' ').split(' ').length;
if (wordCount == max) {
// Reached max, prevent additional.
e.preventDefault();
} else if (wordCount > max) {
<!--Edited to show code from user3003216-->
<!--Isn't working like this, textarea doesn't update.-->
var overage = wordCount - max;
var words = value.split(' ');
for(var i = 0; i<overage; i++){
words.pop();
}
}
});
});
The easiest way to approach this is just to count the number of words on keypress and go from there. Check whether there are more words than the amount allowed. If so, remove all the excess words: while (text.length > maxWords). Then just replace the value of the text box with the updated text.
fiddle
JavaScript
var maxWords = 10;
$("#myText").keypress(function (event) {
var text = $(this).val().split(" "); // grabs the text and splits it
while (text.length > maxWords) { // while more words than maxWords
event.preventDefault();
text.pop(); // remove the last word
// event.preventDefault() isn't absolutely necessary,
// it just slightly alters the typing;
// remove it to see the difference
}
$(this).val(text.join(" ")); // replace the text with the updated text
})
HTML
<p>Enter no more than 10 words:</p>
<textarea id="myText"></textarea>
CSS
textarea {
width: 300px;
height: 100px;
}
You can easily test whether it works by pasting more than maxWords—in this case, 10—words into the textarea and pressing space. All the extra words will be removed.
You can put below code into your else if statement..
else if (wordCount > max) {
var overage = wordCount - max;
var words = value.split(' ');
for(var i = 0; i<overage; i++){
words.pop();
}
}
And if you want to get your string back from that words, you can use join like below:
str = words.join(' ');
well it would be better to use java script so here you go:
var maxWords = 20;
event.rc = true;
var words = event.value.split(" ");
if (words.length>maxWords) {
app.alert("You may not enter more than " + maxWords + " words in this field.");
event.rc = false;
}
jsFiddle Demo
You can use val to re-value the text-box. The array slice method will allow you to pull the first 100 words out of the array. Then just join them with a space and stick them back in the text-box.
$(document).ready(function($) {
var max = 100;
$('#text').keyup(function(e) {
if (e.which < 0x20) {
return;
}
var value = $('#text').val();
var words = value.trim().split(/\s+/gi);
var wordCount = words.length;
if (wordCount == max) {
// Reached max, prevent additional.
e.preventDefault();
} else if (wordCount > max) {
var substring = words.slice(0, max).join(' ');
$("#text").val(substring + ' ');
}
});
});
While you've already accepted an answer I thought I might be able to offer a slightly more refined version:
function limitWords(max){
// setting the value of the textarea:
$(this).val(function(i,v){
// i: the index of the current element in the collection,
// v: the current (pre-manipulation) value of the element.
// splitting the value by sequences of white-space characters,
// turning it into an Array. Slicing that array taking the first 10 elements,
// joining these words back together with a single space between them:
return v.split(/\s+/).slice(0,10).join(' ');
});
}
$('#demo').on('keyup paste input', limitWords);
JS Fiddle demo.
References:
JavaScript:
Array.prototype.join().
Array.prototype.slice().
String.prototype.split().
jQuery:
on().
val().

Using javascript substring() to create a read more link

I'm developing a Classic ASP page that pulls some content from a database and creates a Read more link after the first 100 characters as follows;
<div class="contentdetail"><%=StripHTML(rspropertyresults.Fields.Item("ContentDetails").Value)%></div>
<script type="text/javascript">
$(function() {
var cutoff = 200;
var text = $('div.contentdetail').text();
var rest = $('div.contentdetail').text().substring(cutoff);
if (text.length > 200) {
var period = rest.indexOf('.');
var space = rest.indexOf(' ');
cutoff += Math.max(Math.min(period, space), 0);
}
var visibleText = $('div.contentdetail').text().substring(0, cutoff);
$('div.contentdetail')
.html(visibleText + ('<span>' + rest + '</span>'))
.append('<a title="Read More" style="font-weight:bold;display: block; cursor: pointer;">Read More…</a>')
.click(function() {
$(this).find('span').toggle();
$(this).find('a:last').hide();
});
$('div.contentdetail span').hide();
});
</script>
However, the script obviously just cuts the text off after 100 characters. Preferably I would like it to keep on writing text until the first period or space, for example. Is this possible to do?
Thank you.
var cutoff = 100;
var text = $('div.contentdetail').text();
var rest = text.substring(cutoff);
if (text.length > cutoff) {
var period = rest.indexOf('.');
var space = rest.indexOf(' ');
cutoff += Math.max(Math.min(period, space), 0);
}
// Assign the rest again, because we recalculated the cutoff
rest = text.substring(cutoff);
var visibleText = $('div.contentdetail').text().substring(0, cutoff);
EDIT: shortened it a bit.
EDIT: Fixed a bug
EDIT: QoL improvement
How about:
var text= $('div.contentdetail').text();
var match= text.match( /^(.{100}([^ .]{0,20}[ .])?)(.{20,})$/ );
if (match!==null) {
var visibleText = match[1];
var textToHide = match[3];
...do replacement...
}
The {0,20} will look forward for a space or period for up to 20 characters before giving up and breaking at exactly 100 characters. This stops an extremely long word from breaking out of the length limitation. The {20,} at the end stops a match being made when it would only hide a pointlessly small amount of content.
As for the replacement code, don't do this:
.html(visibleText + ('<span>' + textToHide + '</span>'))
This is inserting plain-text into an HTML context without any escaping. If visibleText or textToHide contains any < or & characters you will be mangling them, perhaps causing a XSS security problem in the process.
Instead create the set the text() of the div and the span separately, since that's the way you read the text in the first place.
Here is a fairly simple approach to getting endings at the word level, and shooting for about your given limit in characters.
var limit = 100,
text = $('div.contentdetail').text().split(/\s+/),
word,
letter_count = 0,
trunc = '',
i = 0;
while (i < text.length && letter_count < limit) {
word = text[i++];
trunc += word+' ';
letter_count = trunc.length-1;
}
trunc = $.trim(trunc)+'...';
console.log(trunc);

Categories