Write/delete text by character - javascript

I try to make change of any text by char by char (show text by char, delete text by char and show another one char by char).
What I actually have?
var i = 0;
var terms = ['text <b>bold</b>', 'longer text <b>bold</b>', '<b>bold</b> text 3'];
var timer = setInterval(function() {
var el = $('#el');
var wr = $('#wr');
setInterval(function() {
var str = el.html(); // doesn't work (still shows all content, not sliced one)
el.html(str.substring(0, str.length - 1));
}, 300 / str.length); // (300 / str.length) - do all animation in 300s
i++;
if (i === 3) {
i = 0;
}
}, 2500);
I have problem with slicing last char, so I don't get to adding new text so far :-(
One of variants I tried:
...
var text = terms[i].split('');
setInterval(function() {
el.html(text); // add sliced text in loop... not working as expected
// ...
text = text.slice(0, -1); // slice text by last character
}, 300 / text.length);
Okay, due to the comments a little bit explanation
I have an element
<span id=el>text <b>bold</b></span>
In 300ms interval I need to remove this text char by char.
<span id=el>text <b>bold</b></span>
<span id=el>text <b>bol</b></span>
<span id=el>text <b>bo</b></span>
<span id=el>text <b>b</b></span>
<span id=el>text <b></b></span> // remove 'b'
<span id=el>text</span> // remove ' ' and empty bold
<span id=el>tex</span>
<span id=el>te</span>
<span id=el>t</span>
<span id=el></span>
// now element is empty, since start it's 300ms
// and now I need to put there new text, char by char (whole phrase 300ms again)
<span id=el>l</span>
<span id=el>lo</span>
<span id=el>lon</span>
...
<span id=el>longer tex</span>
<span id=el>longer text</span>
<span id=el>longer text </span> // add space
<span id=el>longer text <b>b</b></span> // add 'b' into bold
<span id=el>longer text <b>bo</b></span>
<span id=el>longer text <b>bol</b></span>
<span id=el>longer text <b>bold</b></span>
// after 2500ms remove this char by char again and replace by third. Etc.
Etc. Can tou help me with that please? Tried that for last 2 days, many attempts, no result...
Thanks

This is how I would organize my code to shrink and grow an element. The only sensible way I can do this is to first replace < and > by the corresponding entity codes < and > so that these characters are not interpreted as actual tags. These 4-letter entity codes will be removed and added as a single unit. In this way you can shrink the string one quasi-character at a time from right to left and still have valid HTML at all times.
The Promise api (well, acually jQuery's $.Deferred version of this) is used to be able to know in a deterministic fashion when the shrink-grow cycle, which is an asynchronous process, has completed to then start the 2500 ms delay (which is another asynchronous process) before beginning anew.
$(function() {
function shrink_grow(resolve, term)
{
term = term.replace(/</g, '<').replace(/>/g, '>');
let el = $('#el');
el.html(term);
let interval = setInterval(shrinker, 30);
function shrinker()
{
let str = el.html();
let n = str.length >= 4 && (str.endsWith('>') || str.endsWith(`<`)) ? 4 : 1;
el.html(str.substr(0, str.length - n));
if (str.length === 0) {
clearInterval(interval);
interval = setInterval(grower, 30);
}
}
function grower()
{
let str = el.html();
if (str.length == term.length) {
clearInterval(interval);
resolve(undefined); // we are done
}
else if (str.length <= term.length - 4 && (term.substr(str.length + 1, 4) == '<' || term.substr(str.length + 1, 4) == '>')) {
el.html(term.substr(0, str.length + 4));
}
else {
el.html(term.substr(0, str.length + 1));
}
}
}
function pause(milliseconds)
{
// Create a new Deferred object
var deferred = $.Deferred();
// Resolve the Deferred after the amount of time specified by milliseconds
setTimeout(deferred.resolve, milliseconds);
return deferred.promise();
}
let terms = ['text <b>bold</b>', 'longer text <i>italic</i> text', '<b>bold</b> text 3'];
let term_number = 0;
let deferred = $.Deferred();
let promise = deferred.promise();
shrink_grow(deferred.resolve, terms[term_number++]);
promise.then(function() {
pause(2500).then(function() {
let deferred = $.Deferred();
let promise = deferred.promise();
shrink_grow(deferred.resolve, terms[term_number++]);
promise.then(function() {
pause(2500).then(function() {
let deferred = $.Deferred();
let promise = deferred.promise();
shrink_grow(deferred.resolve, terms[term_number++]);
promise.then(function() {
console.log('done');
});
});
});
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span id="el"></span>
And Keeping the tags intact
This is very complicated, however:
$(function() {
let TOTAL_TIME = 300;
function shrink_grow(resolve, term)
{
let el = $('#el');
let matches = term.match(/<([^>])+>(.*?)<\/\1>/); // look for internal tag
let internalTagTextLength = matches ? matches[2].length : 0;
let internalTagText = internalTagTextLength ? matches[2] : '';
let strlen = term.length;
if (matches) {
strlen -= matches[1].length * 2 + 5;
}
let shrinkGrowInterval = TOTAL_TIME / strlen;
if (shrinkGrowInterval < 16) {
shrinkGrowInterval = 16;
}
let interval = setInterval(grower, shrinkGrowInterval);
function shrinker()
{
let str = el.html();
let matches = str.match(/<([^>])+>(.*?)<\/\1>$/); // <i>text</i> at end of string, for example
if (matches) {
let str2 = matches[2];
if (str2.length < 2) { // get rid of entire tag
str2 = matches[0];
let n = str2.length;
let l = str.length - n;
el.html(str.substr(0, l));
if (l === 0) {
clearInterval(interval);
resolve(undefined); // we are done
}
}
else {
let str2a = str2.substr(0, str2.length - 1);
str = str.replace(/<([^>])+>(.*?)<\/\1>$/, '<' + matches[1] + '>' + str2a + '</' + matches[1] + '>');
el.html(str);
}
}
else {
el.html(str.substr(0, str.length - 1));
if (str.length === 0) {
clearInterval(interval);
resolve(undefined); // we are done
}
}
}
function grower()
{
let str = el.html();
if (str.length == term.length) {
clearInterval(interval);
interval = setInterval(shrinker, shrinkGrowInterval);
}
else {
let matches = term.substr(str.length).match(/^<([^>])+>(.*?)<\/\1>/); // start of <i>text</i>, for example?
if (matches) {
let str2 = '<' + matches[1] + '>' + matches[2].substr(0, 1) + '</' + matches[1] + '>';
el.html(str + str2);
}
else {
let matches = str.match(/<([^>])+>(.*?)<\/\1>$/); // <i>text</i> at end of string, for example
if (matches) {
let str2 = matches[2];
let l = str2.length;
if (l == internalTagTextLength) {
el.html(term.substr(0, str.length + 1));
}
else {
let str2a = internalTagText.substr(0, l + 1);
str = str.replace(/<([^>])+>(.*?)<\/\1>$/, '<' + matches[1] + '>' + str2a + '</' + matches[1] + '>');
el.html(str);
}
}
else {
el.html(term.substr(0, str.length + 1));
}
}
}
}
}
let terms = ['text <b>bold</b>', 'longer text <i>italic</i> text', '<b>bold</b> text 3'];
let nTerms = terms.length;
let termNumber = -1;
function callShrinkGrow()
{
if (++termNumber >= nTerms) {
termNumber = 0;
}
let deferred = $.Deferred();
let promise = deferred.promise();
shrink_grow(deferred.resolve, terms[termNumber]);
promise.then(callShrinkGrow);
}
callShrinkGrow();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<span id="el"></span>

Okay according to my understanding, you want to show string/text char by char. So I think this will help you.
var str = "Hello World";
var c = "";
var i = 0;
(function loop(){
if (i++ > str.length-1) return;
c = c + str[i-1];
setTimeout(function(){
$("#charP").text(c);
loop();
}, 100);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<p id="charP"></p>
</div>

Related

How to make .replace script "digit-case-sensitive"?

I have a simple side-project here to help me at work. What I have is a script that will replace a specified number with another.
My problem now though is, I cannot find a way to make it "digit-case-sensitive" (I'm not sure what it's called sorry), meaning, if I want to change the following, they replace only the specified and not anything else. For example:
10 = 80
1 = 75
0 = 65
The problem is, if I replace 10, there's a tendency that it will become 865.
It's changing 10 to 80 first and then the 0 to 65.
Now I really need help as to how do I make the replace script to the specified digit only and not cut the digits or take only half and change them.
Also, how can I make it so that it changes only once for 1 click of button? In this case, it's processing it twice with 1 click of button. It changes 10 to 80 first and then the 0 to 65. What I'd like is run the script only once per click. In this flawed script, it should only be 65 and not process the 0 to 65, since doing so should take 2 clicks.
Here's my sample code, there will be thousands of digits to replace once I move on from this obstacle.
function fixTextarea(textarea) {
textarea.value = textarea.value.replace("", "")
.replaceAll("10", "80")
.replaceAll("1", "75")
.replaceAll("0", "65")
};
function fixtext() {
let textarea = document.getElementById("textarea1");
textarea.select();
fixTextarea(textarea);
}
window.addEventListener('DOMContentLoaded', function(e) {
var area = document.getElementById("textarea1");
var getCount = function(str, search) {
return str.split(search).length - 1;
};
var replace = function(search, replaceWith) {
if (typeof(search) == "object") {
area.value = area.value.replace(search, replaceWith);
return;
}
if (area.value.indexOf(search) >= 0) {
var start = area.selectionStart;
var end = area.selectionEnd;
var textBefore = area.value.substr(0, end);
var lengthDiff = (replaceWith.length - search.length) * getCount(textBefore, search);
area.value = area.value.replace(search, replaceWith);
area.selectionStart = start + lengthDiff;
area.selectionEnd = end + lengthDiff;
}
};
});
<textarea id="textarea1" name="textarea1">10</textarea>
<button onclick="fixtext()">Update</button>
I apologize in advance for not being able to make myself too clear.
You could replace with searching for alternative strings and take the longest first.
For prevent changing the value again on a click, store the orginal value to a data-* attribute.
const
replace = s => s.replace(/10|1|0/g, s => ({ 10: '80', 1: '75', 0: '65' }[s]));
console.log(replace('1010'));
console.log(replace('01'));
You'll have to be a little creative, by changing "10" to something that has special meaning so that the match on "0" won't change it, then change it to your end result:
function fixTextarea(textarea) {
textarea.value = textarea.value.replace("", "")
.replaceAll("10", "xx")
.replaceAll("1", "75")
.replaceAll("0", "65")
.replaceAll("xx", "80")
};
You can do the replace in an if else if else block
function fixTextarea(textarea) {
if(textarea.value === "10"){
textarea.value = textarea.value.replace("", "").replaceAll("10", "80")
}else if(textarea.value === "1"){
textarea.value = textarea.value.replace("", "").replaceAll("1", "75")
}else{
textarea.value = textarea.value.replace("", "").replaceAll("0", "65")
}
};
function fixtext() {
let textarea = document.getElementById("textarea1");
textarea.select();
fixTextarea(textarea);
}
window.addEventListener('DOMContentLoaded', function(e) {
var area = document.getElementById("textarea1");
var getCount = function(str, search) {
return str.split(search).length - 1;
};
var replace = function(search, replaceWith) {
if (typeof(search) == "object") {
area.value = area.value.replace(search, replaceWith);
return;
}
if (area.value.indexOf(search) >= 0) {
var start = area.selectionStart;
var end = area.selectionEnd;
var textBefore = area.value.substr(0, end);
var lengthDiff = (replaceWith.length - search.length) * getCount(textBefore, search);
area.value = area.value.replace(search, replaceWith);
area.selectionStart = start + lengthDiff;
area.selectionEnd = end + lengthDiff;
}
};
});
<textarea id="textarea1" name="textarea1">10</textarea>
<button onclick="fixtext()">Update</button>

Javascript - a problem with a two-step text input word conversion

Here I am making a word conversion tool which changes a certain word X into Y, or X to Y to Z by using javascript.
Progress: HERE
Here is the entire javascript:
var conversion = {
"one":"two",
};
var conversion2 = {
"two":"three",
};
var maxLength = Object.keys(conversion)
.reduce((a, b) => a.length > b.length ? a : b)
.length;
function convert (text) {
var converted = "";
var cur = 0;
while (cur < text.length) {
var testedPhoneme;
var symbol = undefined;
for (var length = maxLength; length > 0; length --) {
testedPhoneme = text.substr(cur, length);
if (conversion[testedPhoneme]) {
symbol = conversion[testedPhoneme];
break; // stop the loop
}
}
if (symbol) {
converted += symbol;
cur += testedPhoneme.length;
}
else {
converted += text[cur]
cur++;
}
}
return converted
}
var maxLength2 = Object.keys(conversion2)
.reduce((a, b) => a.length > b.length ? a : b)
.length;
function convert2 (text) {
var converted2 = "";
var cur2 = 0;
while (cur2 < text.length) {
var testedPhoneme2;
var symbol2 = undefined;
for (var length = maxLength2; length > 0; length --) {
testedPhoneme2 = text.substr(cur2, length);
if (conversion2[testedPhoneme2]) {
symbol2 = conversion2[testedPhoneme2];
break; // stop the loop
}
}
if (symbol2) {
converted2 += symbol2;
cur2 += testedPhoneme2.length;
}
else {
converted2 += text[cur2]
cur2++;
}
}
return converted2
}
function onInputTextChange(txt) {
var outputTextareaElem = document.getElementById("output_textarea");
var div = document.createElement("div");
var outputHtmlEntities = convert(txt);
div.innerHTML = outputHtmlEntities;
outputTextareaElem.value = div.innerText;
}
function onOutputTextChange(txt) {
var outputTextareaElem2 = document.getElementById("output_textarea2");
var div = document.createElement("div");
var outputHtmlEntities2 = convert2(txt);
div.innerHTML = outputHtmlEntities2;
outputTextareaElem2.value = div.innerText;
}
In the page that I made so far, there are three <textarea>s; Input, Output and Output2.
Currently, thanks to this piece of code;
var conversion = {
"one":"two",
};
var conversion2 = {
"two":"three",
};
If one is typed into Input, Output renders two. If two is manually typed into Output, three gets rendered in Output2.
Here is the problem, I want to render three in Output2 only through typing one into Input, but a direct two-step conversion seems unavailable yet. In other words, Input > Output (one > two) and Output > Output2 (two > three) conversion is available, but Input > Output > Output2 (one > two > three) is unavailable.
What needs to be done to solve this? Any help would be appreciated.
Ok, not exactly what you asked, but I could do something that works
Here is the example : https://jsfiddle.net/alias_gui3/8knw57u0/94/
how to use it :
to add new characters OR new languages/scripts
just complete the dictionnary :
var dictionnary = [
{
latin: "u",
japanese: "う",
emoji: "👋"
// add any script for every characters
},{
latin: "ku",
japanese: "く",
emoji: "👀"
},{
latin: "tsu",
japanese: "つ",
emoji: "🤖"
}
// add any character with the same format
]
to add new textareas :
give your textarea a recognizable id (eg. id="cyrillic")
then connect your textarea with this method:
// connect your textareas below !!
addTextArea(
document.querySelector("#latin"),
"latin"
);
addTextArea(
document.querySelector("#japanese"),
"japanese"
);
addTextArea(
document.querySelector("#emoji"),
"emoji"
);
// add new textarea with a new language here
then all the connections are done, you can edit all your textareas, if they recognise a character they will translate it in all the other textareas
full code
var dictionnary = [
{
latin: "u",
japanese: "う",
emoji: "👋"
// add any script for every characters
},{
latin: "ku",
japanese: "く",
emoji: "👀"
},{
latin: "tsu",
japanese: "つ",
emoji: "🤖"
}
// add any character with the same format
]
// calculate the max length for each language :
var max = {}
dictionnary.forEach(
char => {
Object.keys(char).forEach(script => {
max[script] = max[script]
? char[script].length > max[script]
? char[script].length
: max[script]
: char[script].length
})
}
)// now max contains the maximum length of sequence
// for each language
function findSymbol (
originSymbol,
originScript,
destinationScript
) {
for (var i = 0; i < dictionnary.length; i++) {
var char = dictionnary[i];
if (char[originScript] === originSymbol) {
return char[destinationScript]
}
}
return false // if not found
}
function translate (
text,
originScript,
destinationScript
) {
var cur = 0;
var translated = "";
var maxLength = max[originScript]
while (cur < text.length) {
var testedPhoneme;
var symbol = false;
for (var length=maxLength; length > 0; length--) {
testedPhoneme = text.substr(cur, length);
symbol = findSymbol(
testedPhoneme,
originScript,
destinationScript
)
if (symbol) {
break; // stop the loop
}
}
if (symbol) {
translated += symbol;
cur += testedPhoneme.length;
}
else {
translated += text[cur];
cur++;
}
}
return translated
}
var textareas = []; // the list of your textareas
function addTextArea(element, originScript) {
textareas.push({
element: element,
script: originScript
})
element.addEventListener("input", function (e) {
signalTextChanged(element, originScript)
});
}
function signalTextChanged (
originElement,
originScript
) {
var originText = originElement.value;
textareas.forEach(function (textarea) {
if (textarea.element !== originElement) {
var translated = translate(
originText,
originScript,
textarea.script
)
textarea.element.value = translated;
}
})
}
// connect your textareas below !!
addTextArea(
document.querySelector("#latin"),
"latin"
);
addTextArea(
document.querySelector("#japanese"),
"japanese"
);
addTextArea(
document.querySelector("#emoji"),
"emoji"
);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript">
</script>
</head>
<body>
<center>
<h1>Latin to foreign script converter 3</h1>
<p>type in u, ku, tsu in the FIRST panel</p>
<textarea
id="latin"
autofocus=""
placeholder="type text in latin ! (u ku or tsu)"
rows="10"
style="width:300px"></textarea>
<textarea
id="japanese"
rows="10"
placeholder="type text in japanese !"
style="width:300px"></textarea>
<textarea
id="emoji"
rows="10"
placeholder="type text in emojis !!"
style="width:300px"></textarea>
</center>
</center>
</body>
</html>
I'm not sure if I fully understand what you're trying to achieve here
There are some duplications in your code, what if you'll have 10 fields for output, will you create a special function for each of them?
Try to simplify things.
One way would be to loop through all of your lists as follows:
Put all your conversation in a list
var lists = [conversion, conversion2];
Add isInNextList function to check if your text is a key in the next list
function isInNextList(index, key) {
if (lists.length < index) {
return false;
}
return Object.keys(lists[index]).includes(key);
}
change your onInputTextChange function as follow:
function onInputTextChange(txt) {
var index = 0;
var text = txt;
while (isInNextList(index, text)) {
var outputTextareaElem = document.getElementById(
'output_textarea_' + index
);
var div = document.createElement('div');
text = lists[index][text]; //next key
index++;
div.innerHTML = text;
outputTextareaElem.value = div.innerText;
}
}
change your output textarea's ids to contain the index
id="output_textarea_0"
id="output_textarea_1"
There are other improvements that can be made like:
Creating the output fields dynamically,
Clear output fields etc.

How can I optimize the below code with less number of line?

I have coded the below piece of code which handle the number bigger than 999 and add a comma to it. For example 1,000 and 1,000,000. Wheare as once the number increase the comma will be placed in the correct position. I used DRY principle and I feel still there is another easy way to handle it.
Now, I want to know if there is much better way than that I did.
Waiting for your opinion.
Thanks.
function seperate_num_by_comma() {
var num = '199228754645.25',
withOutComma = num.split('.'),
addNewCommaAfter = 3,
x = withOutComma[0].length % addNewCommaAfter,
lenOfWithOutComma_0 = withOutComma[0].length,
length_1 = withOutComma[0].length - x,
starter = 0,
wholeNumber = ' ';
for (var i = 0; i < lenOfWithOutComma_0; i++) {
function run_first_func() {
wholeNumber += withOutComma[0].substr(starter, addNewCommaAfter);
};
function run_second_fun() {
wholeNumber += withOutComma[0].substr(starter, addNewCommaAfter) + ",";
starter += addNewCommaAfter;
length_1 -= addNewCommaAfter;
};
if (x > 0) {
if (length_1 == 0) {
run_first_func();
break;
} else if (wholeNumber == ' ') {
wholeNumber += withOutComma[0].substr(starter, x) + ",";
length_1 -= addNewCommaAfter;
starter = x;
} else {
run_second_fun();
}
} else if (x == 0) {
if (length_1 == 3) {
run_first_func();
break;
}
run_second_fun();
}
}
console.log(wholeNumber + '.' + withOutComma[1]);
}
seperate_num_by_comma();
One Line:
165B minified version (no IE): seperate_by_comma=t=>(x=(""+t).split("."),z=x[0].replace(/((\d{3})*?)(\.|$)/,"|$1").split("|"),x[0]=z[0]+z[1].replace(/(.{3})/g,",$1").slice(!z[0].length),x.join("."))
OR 180B, IE-friendly: seperate_by_comma=function(t){x=(""+t).split(".");z=x[0].replace(/((\d{3})*?)(\.|$)/,"|$1").split("|");x[0]=z[0]+z[1].replace(/(.{3})/g,",$1").slice(!z[0].length);return x.join(".")}
Explanation:
seperate_num_by_comma = function(number){//12345678.9
var str = String(t);
var withoutDot = str.split("."); //["12345678", "9"]
var chunksOfThree = withoutDot[0].replace(/((\d{3})*?)(\.|$)/,"|$1");//"12|345678"
chunksOfThree = chunksOfThree.split("|");//["12", "345678"]
chunksOfThree[1] = chunksOfThree[1].replace(/(.{3})/g,",$1") //",345,678"
if(chunksOfThree[0].length==0){
chunksOfThree[1] = chunksOfThree[1].slice(1); //Fix for specific cases
}
withoutDot[0] = chunksOfThree[0] /*"12"*/ + chunksOfThree[1] /*",345,678"*/
return withoutDot.join(".") //"12,345,678.9"
}

How to add a new line on a script

Hello I don't know if my title is helpful at all but here is my problem I want to make a type writer effect in JS, CSS, HTML, everything works fine apart from add a new line of text when I try added a new line it dose not show.
var str = "<p>I want to put text here then another line under this one</p>",
<!--var str = "<p>text here</p>",--> <!--This is what I tried to do to add a new line-->
i = 0,
isTag,
text;
(function type() {
text = str.slice(0, ++i);
if (text === str) return;
document.getElementById('typewriter').innerHTML = text;
var char = text.slice(-1);
if( char === '<' ) isTag = true;
if( char === '>' ) isTag = false;
if (isTag) return type();
setTimeout(type, 80);
}());
#typewriter {
color: lime;
text-align: center;
}
<div id="typewriter"></div>
var str = "My text\nSome more text";
var stra = str.split("");
var tw = document.getElementById("output");
function type(){
var char = stra.shift();
if (char){
tw.innerHTML += char;
setTimeout(type, 80);
}
}
type();
<pre id="output"></pre>
use <br />
var str = "<p>I want to put text here<br /> then another line under this one</p>";
Another possibility is to group paragragh elements using span and add display style property of span to block.
window.onload = function () {
var str = "<p><span>I want to put text here then another line under this one</span><span>text here</span></p>";
(function type(isInTagArg, indexArg) {
var index = indexArg || 0;
if (index >= str.length)
return;
var isInTag = isInTagArg || false;
if (isInTag == false) {
if (str.charAt(index) == '<') {
return type(true, index + 1);
} else {
document.getElementById('typewriter').innerHTML = str.substr(0, index + 1);
}
} else {
if (str.charAt(index) == '>') {
return type(false, index + 1);
}
return type(true, index + 1);
}
setTimeout(function() {type(false, index + 1)}, 80);
}());
}
#typewriter {
color: lime;
text-align: center;
}
#typewriter span
{
display: block;
}
<div id="typewriter"></div>

Truncate width function not working when passing integer

I'm trying to create a universal function that I can call from multiple places to truncate long text recursively to fit a predefined pixel width - using jquery.
Here is the code...
function constrain(text, original, ideal_width){
var ideal = parseInt(ideal_width);
$('span.temp_item').remove();
var temp_item = ('<span class="temp_item" style="display:none">'+ text +'</span>');
var item_length = text.length;
$(temp_item).appendTo('body');
var item_width = $('span.temp_item').width();
if (item_width > ideal) {
var smaller_text = text.substr(0, (item_length-1));
return constrain(smaller_text, original);
} else if (item_length != original) {
return (text + '…');
} else if (item_length == original) {
return text;
}
}
If I run the function like this:
$('.service_link span:odd').each(function(){
var item_text = $(this).text();
var original_length = item_text.length;
var constrained = constrain(item_text, original_length,'175');
$(this).html(constrained);
});
The text doesn't truncate. I also tried the 175 without the quotes.
If I define var ideal = 175; inside the function, then it works. Why is passing 175 to the function not working? I did a parseInt on it in case it was a string.
Also - this truncate code run a bit slow on older machines - any tips for speeding it up?
Thanks!
Great stuff here. I used the function by Phil Carter. I just wanted the new string with the &hellip to be truncated at the same width as the rest.
I just quickly added another temp-width lookup and recursive call. Could use some cleanup but it works.
here's the new while:
while(item_width > ideal) {
var smaller_text = text.substr(0, (item_length-1));
return constrain(smaller_text, original, ideal_width, counter);
}
if (item_length != original) {
new_text=text+'…';
$('span.temp_item').remove();
var temp_item = ('<span class="temp_item" style="display:none">'+ new_text +'</span>');
$(temp_item).appendTo('body');
var item_width_new = $('span.temp_item').width();
if(item_width_new>ideal){
var smaller_text = text.substr(0, (item_length-1));
return constrain(smaller_text, original, ideal_width, counter);
}
else {
return new_text;
}
} else if (item_length == original) {
return text;
}
}
What happens when the visitor to your site presses "ctl +" ? It's my (probably out of date) belief that you're supposed to use "em" sizes for font containers, so they scale.
Ah... found the bug - forgot to pass the recursive part the ideal width:
return constrain(smaller_text, original, ideal);
TOTAL WE WRITE
So I decided that your iteration over the lorum ipsum text in 5 spans, taking 16 secs was far too long, so thought how to speed this up. and I have it down to 0.4 seconds.
function constrain(text, original, ideal_width, counter){
var ideal = parseInt(ideal_width);
$('span.temp_item').remove();
var temp_item = ('<span class="temp_item" style="display:none">'+ text +'</span>');
var item_length = text.length;
$(temp_item).appendTo('body');
var item_width = $('span.temp_item').width();
if(counter == 0) {
//work out some ranges
var temp_item = ('<span class="temp_item_i" style="display:none">i</span>');
$(temp_item).appendTo('body');
var i_width = $('span.temp_item_i').width();
var max_i = Math.round((ideal_width / i_width) + 1);
var temp_item = ('<span class="temp_item_m" style="display:none">M</span>');
$(temp_item).appendTo('body');
var m_width = $('span.temp_item_m').width();
var max_m = Math.round((ideal_width / m_width) + 1);
text = text.substr(0, (max_i - max_m));
var item_length = text.length;
}
counter++;
while(item_width > ideal) {
var smaller_text = text.substr(0, (item_length-1));
return constrain(smaller_text, original, ideal_width, counter);
}
if (item_length != original) {
return (text + '…');
} else if (item_length == original) {
return text;
}
}
$(document).ready(function() {
var d = new Date();
var s = d.getTime();
$('.service_link').each(function(){
var item_text = $(this).text();
var original_length = item_text.length;
var constrained = constrain(item_text, original_length, 175, 0);
$(this).html(constrained);
});
var e = d.getTime()
alert('Time Taken: ' + ((e - s)/1000));
});
Basically on the first run, it works out how many lowercase i's and how many uppercase Ms fit in the space, and then restricts the text length to that, this reduces the number of iterations massively.
Hope this helps.

Categories