let´s say I have a string in JavaScript:
var str = '<span class="color:red;">Hello</span> my name is <span class="color:red;">Julian</span>';
So I would like to print each 300ms one character so that it looks as if it is being entered. Sure I can make a for-loop for each character and print it inside an element, but the problem is the formatting. If I use the for-loop it will even print the span-tag separately, but that will causing problems.
How to print every character after a while with formatting?
This quite an evil trick but you can use a white div on top of your string and move it step by step every 300ms. In this way a letter appears every 300ms. The only problem is to determine how big each step needs to be since the width of each character will vary.
A way to determine the width is to load all the characters separate in a div and measure the width. Of course you first need to strip the html. In order to so you could use How to strip HTML tags with jQuery?
You could split all characters into an array and then loop like this:
var str = '<span class="red">Hello</span> my name is <span class="red">Julian</span>',
AllChars = [],
SetTxt = true,
newstr = '';
for (var i = 0; i < str.length; i++) {
newstr += str.substr(i,1);
if((str.substr(i,1) == '<') || (str.substr(i,1) == '&')){
SetTxt = false;
}else if(SetTxt){
AllChars.push(newstr);
}else if((str.substr(i,1) == '>') || (str.substr(i,1) == ';')){
if(str.length == (i+1)){
AllChars.push(newstr);
}
SetTxt = true;
}
}
for (var i in AllChars){
setTimeout(function(i){
$('#text').html(AllChars[i]);
},300 * i,i);
}
Check the jsfiddle for a working example: http://jsfiddle.net/2R9Dk/1/
You need to parse html tags and text separately. Something like:
var str = '<span class="colored">Hello</span> my name is <span class="colored bold">Julian</span>';
function printTextByLetter(text, selector, speed) {
var html = text.match(/(<[^<>]*>)/gi),
sel = selector || 'body',
arr = text.replace(/(<[^<>]*>)/gi, '{!!}').match(/(\{!!\}|.)/gi),
counter = 0, cursor = jQuery(sel), insideTag,
interval = setInterval(printChar, speed);
function printChar() {
if(arr[0]){
if(arr[0] === '{!!}') {
if(!insideTag) {
insideTag = true;
cursor.append(html[0], html[1]);
html.shift();
html.shift();
cursor = cursor.children().eq(counter);
} else {
insideTag = false;
cursor = cursor.parent();
counter++;
}
} else {
cursor.append(arr[0]);
}
arr.shift();
} else {
clearInterval(interval);
}
}
}
// DOM ready
jQuery(function($){
printTextByLetter(str, '#target', 300);
});
And don't forget to clear intervals - it does affect performance.
Example on JSFiddle: http://jsfiddle.net/36kLf/7/
Related
I have gotten this far thanks to a lot of searching, but I am stuck on a way to format the link only and not the text. I would like the link to have the numbers in the text, but without spaces, parenthesis, periods or the minus symbol and leave the text as is. The link should have as an example 555 123-1234
<div id="phonedirectory">
<ul>
<li>Phone 1 - 555 123-1234</li>
<li>Phone 2 - 555.123.4321</li>
<li>Phone 3 - (555) 123-6789</li>
</ul>
</div>
<script type="text/javascript">
//<![CDATA[
$(window).load(function(){
var regex = /\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}/g;
var text = $("body:first").html();
text = text.replace(regex, "$&");
$("body:first").html(text);
});
//]]>
</script>
Anyone have any ideas?
Whatever you do, don't (!) regex-replace the entire HTML of your page. Ever. This is asking for trouble.
The correct approach is more complicated. But in return it's... well... correct.
var phonePattern = /\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}/g,
phoneReplacement = '$&';
function replacePhoneNumbers(container) {
$(container).contents(":not(a)").each(function () {
var $this = $(this);
if (this.nodeType === 3) {
$(this).replaceWith( $this.text().replace(phonePattern, phoneReplacement) );
} else {
return replacePhoneNumbers(this);
}
});
}
$(function () {
replacePhoneNumbers(document.body);
});
The function is recursive. It inspects the entire container you give it (document.body in this case) and replaces text nodes specifically (nodeType === 3) but only if they are not already part of a link.
This way only those parts of the document are treated that acutally need treatment. The rest of the document is kept unchanged. No re-renders occur, no layout changes and no risk of breaking the document tree if you mess up the regular expression.
You can even roll that into a jQuery plugin if you want.
$.fn.extend({
phonelinkify: function (pattern, replacement) {
return this.contents().each(function () {
var $this = $(this);
if (this.nodeType === 3 && $this.parents("a").length === 0) {
$(this).replaceWith( $this.text().replace(pattern, replacement) );
} else {
return $(this).phonelinkify(pattern, replacement);
}
});
}
});
and
$(function () {
$("#phonedirectory").phonelinkify(/\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}/g, '$&');
});
To make a more custom replacement, change the phoneReplacement variable to a function.
phoneReplacement = function (num) {
return '' + num + '';
};
This will turn Phone 3 - (555) 123-6789 into
Phone 3 - (555) 123-6789
This works with both the original and the plugin version of this answer. Read up on .replace() to understand how passing functions as a replacement works.
Disclaimer: Whether the regular expressions in use here are completely appropriate for phone number matching or not (I very much doubt they are) is beyond the scope of this answer.
Honestly I would do this without using a regex.
var numOnly = function (inputString) {
var validChars = "0123456789";
var outputString = '';
var len = inputString.length;
for (var i = 0; i < len; i++) {
if (validChars.indexOf(inputString[i]) > -1) {
outputString += inputString[i];
}
}
return outputString;
}
var phoneNumber = numOnly(phoneNumberString);
Are you trying to do this?
$('li').html(function (i, html) {
var text = html.split(' - ').slice(1).join(' - '),
num = text.match(/\d+/g).join('');
return '' + text + '';
});
Is it possible to have jquery/javascript insert sequential line number at the start of all lines in a paragraph and, better still, to follow the sequence through to subsequent paragraphs?
I want to be able to refer students quickly to particular lines of an article (in a classroom setting). I have lots of articles to which I would like to apply this functionality, each of which has varying numbers of paragraphs.
I was hoping this might be possible, even in a responsive page, where the width of the paragraphs changes, depending on the display device, and the consequent number of lines in each paragraph becomes greater or fewer.
Thanks in advance to anyone who can help.
Here is one approach that may suit your purposes.
Get the height of a one-line paragraph, for reference.
For each paragraph, get the actual height, and infer the number of lines.
Loop through the lines and add the numbering at absolute positions.
var refHeight = $("p").eq(0).height();
$("p").eq(0).remove();
var cnt = 1;
$("p").each(function(index) {
var pos = $(this).position();
var h = $(this).height();
var lines = h / refHeight;
var lineHeight = h / lines;
for (var i=pos.top ; i<pos.top+h ; i += lineHeight) {
var num = $("<p>", { class: "number" });
num.text(cnt++);
num.css("top", i);
$(this).before(num);
console.log(i);
}
});
(Fiddle)
Edit
If you wanted to use a fixed line length (so that everyone is seeing the same numbers), you could combine the above with the following:
Break the paragraphs into lines.
Wrap each line in a span/div, and re-append.
Block the browser from text wrapping.
$("p").each(function() {
var text = $(this).text();
$(this).html("");
var i=0;
while (i<text.length) {
lineCharWidth = charWidth;
while (i+lineCharWidth < text.length && text[i+lineCharWidth] != ' ') {
lineCharWidth++;
}
var line = $("<span>", { class: "line" }).text(text.substr(i, lineCharWidth));
$(this).append(line).append("<br/>");
i += lineCharWidth;
}
});
(Fiddle)
Here's a solution that uses a function to split the paragraph text on space characters based on a pre-determined line length and then replaces the text with an <ol> comprised of <li> elements each containing one line of text:
var lineNum = 1;
function splitLines(text, lineLen) {
var words = text.split(/\s/g), line = '', lines = [];
$.each(words, function(idx) {
line += this + ' ';
if (line.length > lineLen || idx == words.length - 1) {
lines.push(line);
line = '';
lineNum += 1;
}
});
return lines;
}
$('p').each(function() {
var $p = $(this), $ol = $('<ol start="' + lineNum + '">'), lineLen = 50;
$.each(splitLines($p.text(), lineLen), function(idx) {
$ol.append('<li>' + this + '</li>');
});
$p.text('').append($ol);
});
I'm not sure about the support for the start attribute of the <ol>. It does work in Chrome. Even still, I like using the list element because it's a little more semantically meaningful, in my opinion.
Sure. Just make sure you're encoding your line returns and use it to split up the text with a simple replace.
Sample text:
The quick
brown fox
jumped over
the lazy dog
for this, the actual string would be the following:
The quick\r\nbrown fox\r\njumped over\r\nthe lazy dog
I think something like this would work (without the document.write, and there could be performance improvements):
var input = '\r\nThe quick\r\nbrown fox\r\njumped over\r\nthe lazy dog';
input = input.replace(/\r\n/g, '<div class=\'index\'></div>');
document.write(input);
var idx = 0;
$('.index').each(function(){
$(this).text(idx++);
});
If I'm not mistaken, this should write out an index number on each line. Could use some testing/debugging, though :)
For an example of how this is done, check out Github's diff pages.
I have this Javascript code below that searches for any word entered into a textfield. Now, the text that needs to be searched through contains special characters like the apostrophe and dot in this sample text: "And the tribe of Zeb′u·lun."
Now, how can I adopt my JS code to include those special characters? If I type Zebulun with no special characters in my textfield the search function cannot find it.
var SearchResultCount = 0;
var a = new Array();
var oneTime = false;
// helper function, recursively searches in elements and their child nodes
function HighlightAllOccurencesOfStringForElement(element,keyword) {
if (element) {
if (element.nodeType == 3) { // Text node
while (true) {
var value = element.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword;
if (idx < 0) break; // not found, abort
var span = document.createElement("span");
var text = document.createTextNode(value.substr(idx,keyword.length));
span.appendChild(text);
span.setAttribute("class","MyAppHighlight");
text = document.createTextNode(value.substr(idx+keyword.length));
element.deleteData(idx, value.length - idx);
var next = element.nextSibling;
element.parentNode.insertBefore(span, next);
element.parentNode.insertBefore(text, next);
element = text;
span.scrollIntoView();
span.style.background= "-webkit-linear-gradient(top, #FAE309, #FFF7AA)";
span.style.fontWeight = "bold";
span.style.padding = "2px";
span.style.borderRadius = "5px";
span.style.boxShadow = "0px 0px 2px black";
a.push(span); // SET THIS CODE HERE
SearchResultCount++; // update the counter
}
} else if (element.nodeType == 1) { // Element node
if (element.style.display != "none" && element.nodeName.toLowerCase() != 'select') {
for (var i=element.childNodes.length-1; i>=0; i--) {
HighlightAllOccurencesOfStringForElement(element.childNodes[i],keyword);
}
}
}
}
}
// the main entry point to start the search
function HighlightAllOccurencesOfString(keyword) {
RemoveAllHighlights();
HighlightAllOccurencesOfStringForElement(document.body, keyword.toLowerCase());
}
First off, there's a closing parenthesis missing in the following line:
var idx = value.toLowerCase().indexOf(keyword;
So I wouldn't be surprised if the function didn't work at all.
To answer your question, one way to do this is to wash out every character except alphabetic characters using the String variable's native replace() function. You'd have to do this with both the search term and the text you're searching, so you'll have to pass both your value and your keyword variables through the function. Something like this:
keyword = cleanUp(keyword);
var value = cleanUp(element.nodeValue);
...
function cleanUp(toClean) {
cleaned = toClean.replace([^a-zA-Z],""); //Deletes non-alphabetic characters (including spaces) by replacing them with nothing. If you want to leave spaces intact, use [^a-zA-Z ] instead.
return cleaned;
}
Once this is done, use the same function you've got going to compare the two strings.
In my HTML source code, I have a code block like the following (I use showdown and highlight.js on this page):
<pre><code class="cpp">
double myNumber = (double)4;
</code></pre>
My problem is that the first linebreak remains part of the "code" block. It's probably because of the enclosing "pre" block, but I need that there because highlight.js expects it (also apparently the HTML5 standard recommends it). The code is rendered like this (note the leading line break):
So my question is, using css, javascript or jquery, how can I remove leading or trailing linebreaks from "code" blocks like this one?
You could use this hack:
pre:first-line {
line-height: 0;
}
Here's another approach using Javascript that also solves the problem:
<script>
window.onload = function (){
// remove leading linebreaks from code blocks.
var pre = document.getElementsByTagName("code");
for (var i = 0, len = pre.length; i < len; i++) {
var text = pre[i].firstChild.nodeValue;
pre[i].firstChild.nodeValue = text.replace(/^\n+|\n+$/g, "");
}
}
</script>
Someone else posted this, then deleted their answer, but I thought it was worth preserving.
That's how pre works by default: it honors line breaks and whitespace. If you don't want the newline to render, then you have to remove it. Either outright remove it from the source or comment it out if you care how the source looks.
http://jsfiddle.net/VL8tG/
<pre><code class="cpp"><!--
-->double myNumber = (double)4;<!--
--></code></pre>
I just remove them manually before the highlighter starts.
const trimLine = node =>
node.firstChild.nodeValue = node.firstChild.nodeValue.replace(/^\n+|\n+$/g, "");
window.onload = function () {
Array.prototype.slice
.call(document.getElementsByTagName("code"), 0)
.map(trimLine);
hljs.initHighlightingOnLoad();
}
Demo: http://jsfiddle.net/WjVVs/4/.
Tested with Chrome and FF on the PC. It does not work in IE 9 when the plugin is applied to a code element (it appears to work fine when applied to a pre element). I can't find a suitable workaround, but feel free to comment/update.
This is a modified version from another answer. This plugin attempts to remove extra indentation caused by the natural flow of the document. I've modified it to be smarter about leading whitespace.
If it works correctly, you should see something like:
Usage
$("code").prettyPre(); // any selector here
HTML with leading whitespace and extra indentation
<div>
<pre><code class="cpp">
double myNumber = (double)4;
// another line
// another line
// this is purposely indented further
for( var i = 0; i < 100; i++ ){
}
</code></pre>
</div>
Plugin
(function( $ ) {
$.fn.prettyPre = function( method ) {
var defaults = {
ignoreExpression: /\s/ // what should be ignored?
};
var methods = {
init: function( options ) {
this.each( function() {
var context = $.extend( {}, defaults, options );
var $obj = $( this );
var usingInnerText = true;
var text = $obj.get( 0 ).innerText;
// some browsers support innerText...some don't...some ONLY work with innerText.
if ( typeof text == "undefined" ) {
text = $obj.html();
usingInnerText = false;
}
// use the first line as a baseline for how many unwanted leading whitespace characters are present
var superfluousSpaceCount = 0;
var pos = 0;
var currentChar = text.substring( 0, 1 );
while ( context.ignoreExpression.test( currentChar ) ) {
if(currentChar !== "\n"){
superfluousSpaceCount++;
}else{
superfluousSpaceCount = 0;
}
currentChar = text.substring( ++pos, pos + 1 );
}
// split
var parts = text.split( "\n" );
var reformattedText = "";
// reconstruct
var length = parts.length;
for ( var i = 0; i < length; i++ ) {
// remove leading whitespace (represented by an empty string)
if(i === 0 && parts[0]=== ""){
continue;
}
// cleanup, and don't append a trailing newline if we are on the last line
reformattedText += parts[i].substring( superfluousSpaceCount ) + ( i == length - 1 ? "" : "\n" );
}
// modify original
if ( usingInnerText ) {
$obj.get( 0 ).innerText = reformattedText;
}
else {
// This does not appear to execute code in any browser but the onus is on the developer to not
// put raw input from a user anywhere on a page, even if it doesn't execute!
$obj.html( reformattedText );
}
} );
}
}
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ) );
}
else if ( typeof method === "object" || !method ) {
return methods.init.apply( this, arguments );
}
else {
$.error( "Method " + method + " does not exist on jQuery.prettyPre." );
}
}
} )( jQuery );
These functions use a class (added to the pre) to remove leading and trailing whitespace
function removeWhitespace(indent) {
// Get a list of all elements that need whitespace removed by indent value (will have class `indent-X`)
// List may be 0 long - loop simply doesn't run
var preElements = document.getElementsByClassName('indent-'+indent);
for (i = 0; i < preElements.length; i++) {
preElements[i].innerHTML = preElements[i].innerHTML.split('\n'+' '.repeat(indent)).join('\n').split('\n'+' '.repeat(indent-2)+'</code>').join('</code>').split("\n").slice(1,-1).join("\n");
//split('\n'+' '.repeat(indent)).join('\n') -- Split at every newline followed by X spaces. Then join together with the newlines.
// .split('\n'+' '.repeat(indent-2)+'</code>').join('</code>') -- The lastline will have 2 less spaces, so remove those, and the newline at the end. Add the tag back in.
//.split("\n").slice(1,-2).join("\n"); -- Remove the first and last lines.
}
}
function removeWhitespaces() {
// Loop over all indents, 2 to 40
for (indent = 2; indent <= 40; indent+=2) {
removeWhitespace(indent);
}
}
Simply add the class indent-X where X is the amount of whitespace you want to remove to the pre.
JSFiddle
function removeWhitespace(indent) {
// Get a list of all elements that need indent removed by indent value (will have class `indent-X`)
// List may be 0 long - loop simply doesn't run
var preElements = document.getElementsByClassName('indent-' + indent);
for (i = 0; i < preElements.length; i++) {
preElements[i].innerHTML = preElements[i].innerHTML.split('\n' + ' '.repeat(indent)).join('\n').split('\n' + ' '.repeat(indent - 2) + '</code>').join('</code>').split("\n").slice(1, -2).join("\n");
//split('\n'+' '.repeat(indent)).join('\n') -- Split at every newline followed by X spaces. Then join together with the newlines.
// .split('\n'+' '.repeat(indent-2)+'</code>').join('</code>') -- The lastline will have 2 less spaces, so remove those, and the newline at the end. Add the tag back in.
//.split("\n").slice(1,-1).join("\n"); -- Remove the first and last lines.
// Remove the clickme element.
document.getElementById('clickme').innerHTML = '';
}
}
function removeWhitespaces() {
// Loop over all indents, 2 to 40
for (indent = 2; indent <= 40; indent += 2) {
removeWhitespace(indent);
}
}
.indent-14 {
background-color: #ccc;
}
<body>
<div id="clickme" onclick="removeWhitespaces()">
Click Me
</div>
<pre class="indent-14">
<code>
function createCORSRequest(method, url) {
var request = new XMLHttpRequest();
if ('withCredentials' in request) {
request.open(method, url, true);
} else if (typeof XDomainRequest != 'undefined') {
request = new XDomainRequest();
request.open(method, url);
} else {
request = null;
}
return request;
}
</code>
</pre>
</body>
I have got a div with words seprated by a comma. How can i arrange them so there is four words per line?
edit: the words are dynamically generated
Use the <pre>...</pre> tag. The text will appear on the screen exactly the way you have formatted it.
Using jQuery to simplify access to the DOM:
var content = $('#mydiv').text(); // get current content from the page
var words = content.split(','); // break into words
for (var i = 3; i < words.length; i += 4) { // every fourth word (but not the first)
words[i] += '<br>'; // append <br>
}
content = words.join(' '); // and rejoin with spaces
$('#mydiv').html(content); // and put it back in the page
NB: I've used .html() to write the contents back out otherwise the <br> tags won't be rendered properly.
Use the BR tag after every four words. See http://www.w3.org/wiki/Html/Elements/br
If str contains the words, then:
var lines = str.match(/([^,]+,*){1,4}/g);
var output = lines === null ? '' : lines.join('<br>');
Example
If:
str = "a,b,c,d,e,f,g,h,i";
Then output will be:
"a,b,c,d,<br>e,f,g,h,<br>i"
Demo here.
Using jQuery, everthing is easier. jQuery is a bunch of tools written in JavaScript and it is all saved to a variable called $. You can call jQuery which will call the base JavaScript for you and make life easier.
Add this to the <head> of your web page
<script type="text/javascript" src="http://code.jquery.com/jquery-1.5.1.min.js"></script>
Add id="arrange" to your div. If you don't like the word arrange, you can use any word you like. Make sure there are no spaces and only letters, numbers, underscores.
<div class="arrange">One two three four one two three four</div>
Add this anywhere after the div that has the text
<script type="text/javascript">
$('div.arrange').each(function () { // For each div with the class="arrange"
var words = this.innerHTML.split(/,/) // Get an array of all the words (separated by comma)
var newHTML = '' // We will add the new contents of the div here.
for(var i = 0; i < words.length; i++) { // For every word
if(i != 0) {// If this is not the first word
if(i % 4 == 0) { // If this is the fourth word
newHTML += '<br/>' // Add a <br/>
} else {
newHTML += ',' // Add a comma
}
}
newHTML += words[i] // Add the word
}
this.innerHTML = newHTML
})
</script>
In this case, I am using jQuery to loop through each div with class="arrange"
You can see a live example here: http://jsfiddle.net/gf5wD/
This example automatically includes jQuery and automatically adds the JavaScript to the end.
You will probably eventually want to save the jquery file to servers you control. The script tag in your head will call the jquery servers which means if their site goes down, yours gets messed up.
Greetings user521180 I got you and i think this code will help
function arrangeDiv() {
var div = document.getElementById('div');
var divinnerHTMLAry = div.textContent.split(' ');
div.innerHTML = "";
var divNewinnerHTML = '';
var count = 0;
for (var i = 0; i < divinnerHTMLAry.length; i++) {
if (divinnerHTMLAry[i] != "" && divinnerHTMLAry[i] != "\n") {
divNewinnerHTML += divinnerHTMLAry[i] + " ";
count += 1;
if (count == 4) {
divNewinnerHTML += "<br />";
}
}
}
div.innerHTML = divNewinnerHTML;
}
Regards :)
edit : this is a PHP solution
Maybe something like that should be able to make what you want.
$string = 'a, b, c, d, e, f';
$array = explode(',', $string,);
foreach($array as $key => $word) {
print trim($word);
if ($key % 4 == 0) {
print '<br />';
}
}
If you get your word in an other way, just arrange the code so you can skip the explode() function.