I have a question concerning my javascript code (Sorry my english is not that good). The code works, however, with only one data entry, the string in the HTML which results, gets cut off near the end (last few characters seems to be dropped, the length of the character drop is consistent). I'm wondering if I'm hitting a string limit or something similar. The problem data entry (which gets stored in the var gDef below) is 4110 characters long which doesn't seem like it should be a problem... Help?
The following is my code:
gI = document.gForm.gLinks.options[document.gForm.gLinks.selectedIndex].value;
definition = "";
gWord = (g[gI].getElementsByTagName("NAME")[0].childNodes[0].nodeValue);
gDef = (g[gI].getElementsByTagName("DEFINITION")[0].childNodes[0].nodeValue).replace(/\n/g,'<br />').replace(/\[/g,'<').replace(/\]/g,'>');
definition += ("<div class=\"word\">");
definition += ("<h3>"+gWord+"</h3>");
definition += ("<div class=\"definition\">"+gDef+"</div>");
definition += ("</div>");
document.getElementById("showGlossary").innerHTML = definition;
Are you sure that the browser isn't hiding the string partially? The CSS property text-overflow can cause this.
Related
I am pulling HTML code from a news page (within the same site) and am wanting to strip all the HTML tags from it. At present, all tags have beens stripped, except for special characters (&") etc. I have scoured all of the stackoverflow posts, and couldn't find anything that works. Perhaps it is the way I am defining the function.
I have tried using multiple variances to the - item.cleanedHtml = item.PublishingPageContent.replace(/</?[^>]+>/gi, '');
But the result either adds the HTML tags back, or doesn't display the text at-all. Within the [] I have tried adding " or just & to see if that would help. I have also tried changing the complete line with varying other suggestions that I could find, to no avail.
_blah.controller('myNewsController', ['$scope','newsService', function($scope,newsService) {
newsService.getNews().then(function(newsItems){
for (var i = 0; i < newsItems.length; i++) {
var item = newsItems[i];
item.cleanedHtml = item.PublishingPageContent.replace(/<\/?[^>]+>/gi, '');
item.cleanedHtmlFun = item.cleanedHtml.replace(/"/gi, '').replace(/'/gi, '');
item.imageUrl = getImageUrlfromSrc(item.File.Properties.PublishingPageImage,item);
}
$scope.news = newsItems;
});
}]);
I expect the output to read: As reported by Tom & Jerry
"What I am getting is: As reported by Tom & Jerry"
I realize that there are several similar questions here but none of the answers solve my case.
I need to be able to take the innerHTML of an element and truncate it to a given character length with the text contents of any inner HTML element taken into account and all HTML tags preserved.
I have found several answers that cover this portion of the question fine as well as several plugins which all do exactly this.
However, in all cases the solution will truncate directly in the middle of any inner elements and then close the tag.
In my case I need the contents of all inner tags to remain intact, essentially allowing any "would be" truncated inner tags to exceed the given character limit.
Any help would be greatly appreciated.
EDIT:
For example:
This is an example of a link inside another element
The above is 51 characters long including spaces. If I wanted to truncate this to 23 characters, we would have to shorten the text inside the </a> tag. Which is exactly what most solutions out there do.
This would give me the following:
This is an example of a
However, for my use case I need to keep any remaining visible tags completely intact and not truncated in any way.
So given the above example, the final output I would like, when attempting to truncate to 23 characters is the following:
This is an example of a link
So essentially we are checking where the truncation takes place. If it is outside of an element we can split the HTML string to exactly that length. If on the other hand it is inside an element, we move to the closing tag of that element, repeating for any parent elements until we get back to the root string and split it there instead.
It sounds like you'd like to be able to truncate the length of your HTML string as a text string, for example consider the following HTML:
'<b>foo</b> bar'
In this case the HTML is 14 characters in length and the text is 7. You would like to be able to truncate it to X text characters (for example 2) so that the new HTML is now:
'<b>fo</b>'
Disclosure: My answer uses a library I developed.
You could use the HTMLString library - Docs : GitHub.
The library makes this task pretty simple. To truncate the HTML as we've outlined above (e.g to 2 text characters) using HTMLString you'd use the following code:
var myString = new HTMLString.String('<b>foo</b> bar');
var truncatedString = myString.slice(0, 2);
console.log(truncatedString.html());
EDIT: After additional information from the OP.
The following truncate function truncates to the last full tag and caters for nested tags.
function truncate(str, len) {
// Convert the string to a HTMLString
var htmlStr = new HTMLString.String(str);
// Check the string needs truncating
if (htmlStr.length() <= len) {
return str;
}
// Find the closing tag for the character we are truncating to
var tags = htmlStr.characters[len - 1].tags();
var closingTag = tags[tags.length - 1];
// Find the last character to contain this tag
for (var index = len; index < htmlStr.length(); index++) {
if (!htmlStr.characters[index].hasTags(closingTag)) {
break;
}
}
return htmlStr.slice(0, index);
}
var myString = 'This is an <b>example ' +
'of a link ' +
'inside</b> another element';
console.log(truncate(myString, 23).html());
console.log(truncate(myString, 18).html());
This will output:
This is an <b>example of a link</b>
This is an <b>example of a link inside</b>
Although HTML is notorious for being terribly formed and has edge cases which are impervious to regex, here is a super light way you could hackily handle HTML with nested tags in vanilla JS.
(function(s, approxNumChars) {
var taggish = /<[^>]+>/g;
var s = s.slice(0, approxNumChars); // ignores tag lengths for solution brevity
s = s.replace(/<[^>]*$/, ''); // rm any trailing partial tags
tags = s.match(taggish);
// find out which tags are unmatched
var openTagsSeen = [];
for (tag_i in tags) {
var tag = tags[tag_i];
if (tag.match(/<[^>]+>/) !== null) {
openTagsSeen.push(tag);
}
else {
// quick version that assumes your HTML is correctly formatted (alas) -- else we would have to check the content inside for matches and loop through the opentags
openTagsSeen.pop();
}
}
// reverse and close unmatched tags
openTagsSeen.reverse();
for (tag_i in openTagsSeen) {
s += ('<\\' + openTagsSeen[tag_i].match(/\w+/)[0] + '>');
}
return s + '...';
})
In a nutshell: truncate it (ignores that some chars will be invisible), regex match the tags, push open tags onto a stack, and pop off the stack as you encounter closing tags (again, assumes well-formed); then close any still-open tags at the end.
(If you want to actually get a certain number of visible characters, you can keep a running counter of how many non-tag chars you've seen so far, and stop the truncation when you fill your quota.)
DISCLAIMER: You shouldn't use this as a production solution, but if you want a super light, personal, hacky solution, this will get basic well-formed HTML.
Since it's blind and lexical, this solution misses a lot of edge cases, including tags that should not be closed, like <img>, but you can hardcode those edge cases or, you know, include a lib for a real HTML parser if you want. Fortunately, since HTML is poorly formed, you won't see it ;)
You've tagged your question regex, but you cannot reliably do this with regular expressions. Obligatory link. So innerHTML is out.
If you're really talking characters, I don't see a way to do it other than to loop through the nodes within the element, recursing into descendant elements, totalling up the lengths of the text nodes you find as you go. When you find the point where you need to truncate, you truncate that text node and then remove all following ones — or probably better, you split that text node into two parts (using splitText) and move the second half into a display: none span (using insertBefore), and then move all subsequent text nodes into display: none spans. (This makes it much easier to undo it.)
Thanks to T.J. Crowder I soon came to the realization that the only way to do this with any kind of efficiency is to use the native DOM methods and iterate through the elements.
I've knocked up a quick, reasonably elegant function which does the trick.
function truncate(rootNode, max){
//Text method for cross browser compatibility
var text = ('innerText' in rootNode)? 'innerText' : 'textContent';
//If total length of characters is less that the limit, short circuit
if(rootNode[text].length <= max){ return; }
var cloneNode = rootNode.cloneNode(true),
currentNode = cloneNode,
//Create DOM iterator to loop only through text nodes
ni = document.createNodeIterator(currentNode, NodeFilter.SHOW_TEXT),
frag = document.createDocumentFragment(),
len = 0;
//loop through text nodes
while (currentNode = ni.nextNode()) {
//if nodes parent is the rootNode, then we are okay to truncate
if (currentNode.parentNode === cloneNode) {
//if we are in the root textNode and the character length exceeds the maximum, truncate the text, add to the fragment and break out of the loop
if (len + currentNode[text].length > max){
currentNode[text] = currentNode[text].substring(0, max - len);
frag.appendChild(currentNode);
break;
}
else{
frag.appendChild(currentNode);
}
}
//If not, simply add the node to the fragment
else{
frag.appendChild(currentNode.parentNode);
}
//Track current character length
len += currentNode[text].length;
}
rootNode.innerHTML = '';
rootNode.appendChild(frag);
}
This could probably be improved, but from my initial testing it is very quick, probably due to using the native DOM methods and it appears to do the job perfectly for me. I hope this helps anyone else with similar requirements.
DISCLAIMER: The above code will only deal with one level deep HTML tags, it will not deal with tags inside tags. Though it could easily be modified to do so by keeping track of the nodes parent and appending the nodes to the correct place in the fragment. As it stands, this is fine for my requirements but may not be useful to others.
I'm following w3school beginner tutorial for JS. There's something I don't understand from code below:
<!DOCTYPE html>
<html>
<body>
<p id="demo"></p>
<script>
var cars = ["Saab","Volvo","BMW"];
var text = "";
for(var i = 0; i < cars.length; i++) {
text+=cars[i] + "<br>";
}
document.getElementById("demo").innerHTML = text;
</script>
</body>
</html>
Can someone explain me the logic of text+=cars[i]? I understand that += means increment, but I can´t understand the logic behind adding the array element to variable text.
Thank you so much for your quick replies! I've got a follow up question: is there an alternative to display the same type of information with having to use the
var text = "";
and
text+=cars[i]
pieces of code? If so, how would the snippet of code look like and what should I insert into HTML if not
text
?
Thanks again!
a+=b is short for a=a+b. In your case you have text = text + cars[i] + "<br>".
text is a string, and you are using + to append a value from the array (that contains strings), and then append "<br>"
The value of text at the end of the loop is going to be
Saab<br>Volvo<br>BMW<br>
where br stands for line break. So that each of them gets printed on new line.
And the last line of code
document.getElementById("demo").innerHTML = text;
changes the value of html element which has id of demo to that of text.
text += cars[i] + '<br>';
Concatenates element i of the cars array to the text, separated by a <br> tag.
Consider it like this,
text+=cars[i] + "<br>";
is actually
text=text+cars[i]+"<br>";
so that rather than deleting the old value it will concatenate the new word with existing string.(String Concatenation).
PS:As a fellow beginner a small piece of advice rather than following W3 Schools please go to site like codecademy which helps you to learn along with practice and proper explanation.
Don't think of += as incrementing, that's ++.
a = a + b
a += b;
Those two statements are the same. The bottom one takes the variable on the left side (a) and appends the right side to it (b), and then assigns it all back to he left side (a). So it's just a shorthand.
So what you're doing in your code is appending the string from cars[i] to your variable text.
This would do the same thing:
text = text + cars[i] + "<br>";
Once the loop runs, you will have the following in text:
Saab<br>Volvo<br>BMW
In javascript + is used for string concatenation
The code
for(var i = 0; i < cars.length; i++) {
text+=cars[i] + "<br>";
}
is picking each element from the array and concatenating "" to it.
If you console log the text before setting the innerHTML, it looks something like this -
"Saab<br>Volvo<br>BMW<br>"
here they do it actually just to show theres no point of doing it this way they just wanna show to reach each individual inside an array and concatanate it into a string the very same thing you can do with Array.prototype.join() method dont think here that u must use as they show always concatanate into a string if you want you can simply use every single individual inside as u wish as well
+= is not increment. It's adding (in this case concatenation) and saving result in the same variable.
var a +=b;
Is the same to:
var = a + b;
In your case script concatenates all array elements into one string and adding <br> tags between them.
I have some code which creates a 'typing text effect' on a page - i.e. javascript takes a string and outputs it on the screen in a way that makes it look like it is being typed. I took the code from a demo here.
The problem is that I want it to output html code, e.g. the output on the screen should be something like:
<html>
<body>
Something here etc etc...
</body>
</head>
In chrome, this works in a rather erratic fashion - sometimes it works perfectly, but other times it doesn't display the first left angle bracket, leaving me with the output of 'html>' rather than '<html>'. In safari, the left angle bracket doesn't display at all. I've tried various things, using '<' instead of the bracket, using unicode, but that everything I do seems to end with the same result.
Here is a github gist of the code, and here is a bl.ocks page to showing it in action. I tried to make a jsfiddle but couldn't get it to run properly, sorry!
Any help is much appreciated, it's been driving me nuts.
Cheers
Nick
Instead of
$span.append(thisLine[letterIndex]);
try
$span.text($span.text() + thisLine[letterIndex]);
Or, per crush's comment below, you could do a string replace:
$span.append(thisLine[letterIndex].replace(/</g,'<').replace(/>/g,'>'));
This works because when you want to display HTML tags, you need to use < and > rather than just < and >, otherwise the browser thinks you're putting in an actual HTML tag, if that makes sense to you. The jquery text method automatically escapes the brackets for you, and in my second example, we're just doing a string replace before passing the string to append.
It looks as if the code appends '<' as '&','l','t',';'. Not verified this but you might want to try:
function typeLetter(lineIndex, letterIndex, $span, line, callback) {
var thisLine = line;
var thisLength = line.length;
var chunk='';
// add the letter
chunk=thisLine[letterIndex];
if ('&'==thisLine[letterIndex]) {
for (var i=1; i<5; i++) {
letterIndex++;
chunk+=thisLine[letterIndex];
if (';'==thisLine[letterIndex] || letterIndex>=thisLength-1) break;
}
}
$span.append(chunk);
...
I have already asked this question in offical Flot google groups, but got no answear. Maybe because it is more javascript oriented, here is the question:
I have added the following code in my code:
var j = "d";
j = j.sub();
plot1 = $.plot($("#grafTemp"), [
{label: "Rosišče (°C): T" + j + "(t) = ---.---°C"
.... the rest does not matter.
And:
legends.eq(i).text(series.label.replace(/=.*/, "= " + y.toFixed(2) +"°C"));
I was using this example:
http://people.iola.dk/olau/flot/examples/tracking.html
Now, the subscript works ok, it displays T_d fine. But when I update
the graph (when user moves mouse over graph), then it displays
<sub>d</sub>
I know that the problem is at the legends.eq(i).text....., where
it returns pure string, with literal:
<sub>
I would like to know, how it would be possible to fix this issue. So it would use html element sub properly?
Glancing at the code, it looks like you'd replace the use of text (e.g., legends.eq(i).text(...)) with html (legends.eq(i).html(...)). But you'd need to be sure that there aren't other generated bits of it that would be a problem (for instance, if this stuff generated a string that had a < or & in it, that would need to be converted to < / & respectively before being fed into the html function).