Loop that changes letter colors - javascript

The purpose is to change color of all characters in #text one by one, I made a loop:
function myFunction() {
var letters = document.getElementById('text');
for (var i = 0; i < letters.innerHTML.length; i++) {
//only change the one you want to
letters.innerHTML = letters.innerHTML.replace(letters[i], '<span style="color: yellow;">'+letters[i]+'</span>');
}
}
It doesnt work but also doesnt show any errors.
https://jsfiddle.net/zkbctk2h/

I suggest to store the text of the element with id = "text" and build a new string out of the old text, because replace would replace the first found character which may not the wanted character, because the replaced character cold contain a character which should not be replaced.
function myFunction() {
var letters = document.getElementById('text'),
text = letters.innerHTML
letters.innerHTML = '';
for (var i = 0; i < text.length; i++) {
letters.innerHTML += '<span style="background-color: yellow;">' + text[i] + '</span>';
}
}
myFunction();
<div id="text">abracadabra</div>
Some typewriter functionality with setInterval and clearInterval
function myFunction() {
var letters = document.getElementById('text'),
text = letters.innerHTML,
i = 0;
return function () {
var j;
if (i < text.length) {
letters.innerHTML = '';
for (j = 0; j <= i; j++) {
letters.innerHTML += '<span style="background-color: yellow;">' + text[j] + '</span>';
}
letters.innerHTML += text.slice(j);
i++;
} else {
clearInterval(interval);
}
}
}
var interval = setInterval(myFunction(), 500);
<div id="text">abracadabra</div>

This is because you are updating the letters, and reading the next letter afterwards. You should use innerText instead of innerHTML because then you only get the text.
Example fiddle: https://jsfiddle.net/zkbctk2h/25/
function myFunction() {
var letters = document.getElementById('text'),
str = letters.innerText,
newString = "";
for (var i = 0; i < str.length; i++) {
//only change the one you want to
newString += '<span style="color: yellow;">'+str[i]+'</span>';
}
letters.innerHTML = newString;
}
I suggest to read once and write once to the dom. If you read and write a force redraw is triggered in the browser. Therefor it can get slow if you have large text.

Just suggesting a more functional approach:
const myFunction = id =>
document.getElementById(id).innerHTML
.split('')
.map(c =>
`<span style="color: green;">${c}</span>`
)
.join('')
document.getElementById('text').innerHTML = myFunction('text')
<div id="text">Hello World</div>

Related

Wrap every word in a span tag concisely

I'm trying to create an inline/copy-pastable anonymous function I can plug into my browser console to wrap every word in a page inside a span element.
Here's what I've tried:
(function() {
document.querySelectorAll("*").each(function() {
var text = this.text();
var words = text.split(' ');
this.innerHTML = '';
for(let word of words) {
this.innerHTML = word.replace(/(\w+)/g, "<span>$1</span>");
}
})
})();
Is there some way to get this to work?
I test it for P tags and it works. I don't recommend run it for all elements :|
(function() {
var elements = document.querySelectorAll("p");
try {
for (i = 0; i < elements.length; i++) {
var text = elements[i].innerHTML;
var words = text.split(' ');
elements[i].innerHTML = '';
for (let word of words) {
elements[i].innerHTML += word.replace(/(\w+)/g, "<span>$1</span>");
}
}
} catch {}
})();

Why I can only get the default value from the input?

I'm super new to JS, and I was writing this little function that supposed to take whatever input from the user and wrap the text at the length of the longest word. All things went well, except I can only get the default value of the input, not the real text someone put there.
My code is:
var inputString = document.getElementById("input").value;
var inputArray = inputString.split(" ");
var outputEl = document.getElementById("output");
// find the number of letters in the longest word:
function longest(inputString) {
let longestWord = 0;
for (var i = 0; i < inputArray.length; i++) {
if (longestWord < inputArray[i].length) {
longestWord = inputArray[i].length
}
}
return longestWord
}
var numberOfLetters = longest(inputString);
// wrap the text
function wrap(input, numberOfLetters) {
let lineLength = 0;
for (var i = 0; i < inputArray.length; i++) {
if (lineLength + inputArray[i].length <= numberOfLetters) {
outputEl.innerHTML += inputArray[i] + " ";
lineLength += inputArray[i].length + 1 // because there's a space
} else {
outputEl.innerHTML += "<br>" + inputArray[i] + " ";
lineLength = inputArray[i].length + 1 // space
}
}
}
<h3>Text Wrapper</h3>
<p id="instructions">We will break your text at the length of your longest word!</p>
<p>Enter Text: <input type="text" id="input" value="put your text here" size='50'></p>
<button onclick="wrap(input.value,numberOfLetters)">Yeah, click me</button>
<p id="output"></p>
if I type input.value in the console, it returns the value I put into; but if I ask for inputString it returns the default value.
I just don't know what's went wrong here? Can somebody help me?
Sorry if it's a stupid mistake, I just couldn't figure it out. Thanks in advance!
welcome to stackoverflow!
as the comments suggest, you need to set your variable values based on the current value of the input. your current code sets the variables at load-time, and so they get set to the default value and stay that way. try:
var outputEl = document.getElementById("output");
// find the number of letters in the longest word:
function longest(inputArray) {
let longestWord = 0;
for (var i = 0; i < inputArray.length; i++) {
if (longestWord < inputArray[i].length) {
longestWord = inputArray[i].length
}
}
return longestWord
}
// wrap the text
function wrap(inputString) {
var inputArray = inputString.split(" ");
var numberOfLetters = longest(inputArray);
let lineLength = 0;
for (var i = 0; i < inputArray.length; i++) {
if (lineLength + inputArray[i].length <= numberOfLetters) {
outputEl.innerHTML += inputArray[i] + " ";
lineLength += inputArray[i].length + 1 // because there's a space
} else {
outputEl.innerHTML += "<br>" + inputArray[i] + " ";
lineLength = inputArray[i].length + 1 // space
}
}
}
<h3>Text Wrapper</h3>
<p id="instructions">We will break your text at the length of your longest word!</p>
<p>Enter Text: <input type="text" id="input" value="put your text here" size='50'></p>
<button onclick="wrap(document.getElementById('input').value)">Yeah, click me</button>
<p id="output"></p>
notice that i moved all the variable instantiation to inside the wrap function. i changed the 'longest' function to accept inputArray as its argument, since that's what it's actually using (not inputString) -- this way it doesn't need a global variable. also i made sure wrap call in the button onclick was identifying the input by its id and pass value, then the 'wrap' function needs only this inputString in order to do its process.
The script block only executes once and that's why you always get the same value when clicking the button. Moving it to the callback function(which executes every time you click) fixes this problem. Here's the working example of what you need:
<body>
<h3>Text Wrapper</h3>
<p id="instructions">We will break your text at the length of your longest word!</p>
<p>Enter Text: <input type="text" id="input" value="put your text here" size='50'></p>
<button onclick="wrap(input.value)">Yeah, click me</button>
<p id="output"></p>
<script>
// find the number of letters in the longest word:
function longest(inputString){
var inputArray = inputString.split(" ");
let longestWord = 0;
for (var i = 0; i < inputArray.length; i++ ) {
if (longestWord < inputArray[i].length) {
longestWord = inputArray[i].length
}
}
return longestWord
}
// wrap the text
function wrap(inputString) {
var numberOfLetters = longest(inputString);
var inputArray = inputString.split(" ");
var inputString = document.getElementById("input").value;
var outputEl = document.getElementById("output");
let lineLength = 0;
for (var i = 0; i < inputArray.length; i++) {
if (lineLength + inputArray[i].length <= numberOfLetters){
outputEl.innerHTML += inputArray[i] + " ";
lineLength += inputArray[i].length + 1 // because there's a space
} else {
outputEl.innerHTML += "<br>" + inputArray[i] + " ";
lineLength = inputArray[i].length + 1 // space
}
}
}
</script>
</body>

Regex: Match a word that is not surrounded by other characters and is also not in an HTML tag

I have no idea what is wrong with my code. For some reason, it matches only phrases that start with the letter s, when it is supposed to match a specific word not surrounded by other word characters, and that is not in an HTML tag. Here is my code:
<!DOCTYPE html>
<html>
<!--
YOUR WINNER REGEX IS...
(?![A-Za-z0-9\-])(?![^<]*>)
Your word before this
-->
<head>
<title>Edititing Tool</title>
<style>
#content {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<h1>EDITING TOOL</h1>
<h3>Paste in text and it SHOULD highlight each word in red</h3>
<div id="content" contenteditable onkeyup="contentchange()"></div>
<script>
var content = document.getElementById("content");//This is what you type into
var words, newText;
function contentchange() {
//Set variables
var contentText = content.innerText.split(/\s/g);//split what's inside the div by all whitespace (tabs, spaces, etc.)
newText = contentText;//make a new variable for that
words = {};
for (var i = 0; i < newText.length; i++) {
//make it all lowercase
newText[i] = newText[i].toLowerCase();
//Remove double-quotes
newText[i] = newText[i].replace('"', "");
//Remove other punctuation except for single quotes (for contractions) and dashes
//The reason this regex is so long is because of google drive and unicode. it really just means /[^\w\s'’-]/g
newText[i] = newText[i].replace(/(?:[\0-\x08\x0E-\x1F!-&\(-,\.\/:-#\[-\^`\{-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2018\u201A-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g,"");
}
//Remove all empty strings
newText = without(newText, "");
//Index everything; Make a variable holding how many times the word is used for every word
for (var i = 0; i < newText.length; i++) {
if (words[newText[i]]) {
words[newText[i]]++;
} else {
words[newText[i]] = 1;
}
}
var contentHTML = decodeHtml(content.innerHTML);//Take away all (because HTML does that)
//Get how many total words you have
var totalWords = 0;
for (var i = 0; i < Object.keys(words).length; i++) {
totalWords += words[Object.keys(words)[i]];
}
for (var i = 0; i < Object.keys(words).length; i++) {
var currentword = Object.keys(words)[i];
contentHTML = contentHTML.replace(new RegExp("(^|\s)("+currentword+")(?![A-Za-z\-])(?![^<]*>)", "ig"), function myFunction(x){return "<span style='color: red'>" + x + "</span>"; console.log("BOOBS LALALALA" + x);});
console.log(currentword);
console.log(words);
console.log(i);
}
content.innerHTML = contentHTML;
}
//Copied from stackoverflow, removes a certain string
function without(array, what) {
return array.filter(function(element) {
return element !== what;
});
}
//Change "September" to "[Ss][Ee][Pp][Tt][Ee][Mm][Bb][Ee][Rr]"
function anycaseRegex(string) {
var returnVal = "";
for (var j = 0; j < string.length; j++) {
returnVal += "[" + string.substring(j, j+1).toUpperCase() + string.substring(j, j+1).toLowerCase() + "]";
}
return returnVal;
}
//Because HTML does that
function decodeHtml(html) {
var txt = document.createElement("textarea");
txt.innerHTML = html;
return txt.value;
}
//PHP REGEX: (?<![A-Za-z0-9\-])[Hh][Ee][Ll][Ll][Oo](?![A-Za-z0-9\-])(?![^<]*>)
</script>
</body>
</html>
Here are a couple of regular expressions that I've used:
/[Hh][Ee][Ll][Ll][Oo](?![A-Za-z\-])(?![^<]*>)/g -- matches hello and ahello, when it should only match hello

How to add margin on targeted letter javascript

I have this in html
<div id="content">
<h1 class="entry-title">"Homepage SlideShow"</h1>
</div>
I also have this in js
var content = document.getElementById("content");
var entryTitle = content.getElementsByClassName('entry-title')[0];
var str = entryTitle.innerHTML;
var newTitle = "";
for (var i = 0; i < str.length; i++) {
if (str[i] == 'e') {
newTitle += str.charAt(i).fontcolor("red");
}
else {
newTitle += str[i];
}
}
entryTitle.innerHTML = newTitle;
I added a color on the targeted letter but I don't have an idea how to add a margin. I want to add margin on the targeted letter like margin = -20px;. I'm new to javascript and I'm hoping someone could help. Thanks
Here's my JSFiddle
Edit:
I have this font that doesn't look good on letter spacing. I don't want to use a span class in my html since I don't want to do it manually on every single page or post that I make.
For example: I want to move all i's to the left since it has the same spacing in any word.
Append with some span element.Then apply the style for that span element using inline style or class name with css
var entryTitle = document.getElementsByClassName('entry-title')[0];
var str = entryTitle.innerHTML;
var newTitle = "";
for (var i = 0; i < str.length; i++) {
if (str[i] == 'e') {
newTitle += '<span class="add">' + str[i] + '</span>'
} else {
newTitle += str[i];
}
}
entryTitle.innerHTML = newTitle;
.add{
/*add your style here*/
color:red;
margin:-20px;
}
<h1 class="entry-title">"Homepage SlideShow"</h1>
var content = document.getElementById("content");
var entryTitle = content.getElementsByClassName('entry-title')[0];
var str = entryTitle.innerHTML;
var newTitle = "";
for (var i = 0; i < str.length; i++) {
if (str[i] == 'e') {
newTitle += "<span style='margin: 20px;'>" + str.charAt(i).fontcolor("red") + "</span>";
} else {
newTitle += str[i];
}
}
entryTitle.innerHTML = newTitle;
<div id="content">
<h1 class="entry-title">"Homepage SlideShow"</h1>
</div>
You can accomplish this with the CSS property letter-spacing apply on a <span> markup.
Here my jsfiddle bro --> http://jsfiddle.net/f6zn0svk/
.letter-spacing { letter-spacing: 10px; }
<h1 class="entry-title">"Hom<span class="letter-spacing">ep</span>age SlideShow"</h1>

Javascript/ActionScript do for each indexOf?

The screenshot below the code shows me issue. It only rainbowfies one instance of text.
How can I get this code to do each [rainbow]specified[/rainbow] text?
It's actually for ActionScript but it works in Javascript too so I've been testing on http://jsfiddle.net
var txt = "This is a [rainbow]test to show that I can[/rainbow] make whatever I want [rainbow]appear as a rainbow[/rainbow] because I am [rainbow]awesome[/rainbow].";
if ((txt.indexOf("[rainbow]") > -1) && (txt.indexOf("[/rainbow]") > -1)) {
var firstChar = txt.indexOf("[rainbow]") + 9;
var lastChar = txt.indexOf("[/rainbow]");
var RAINBOWTEXT = '';
var i = firstChar;
while (i < lastChar) {
RAINBOWTEXT += txt.charAt(i);
i++
}
var text = RAINBOWTEXT;
var texty = '';
colors = new Array('ff00ff', 'ff00cc', 'ff0099', 'ff0066', 'ff0033', 'ff0000', 'ff3300', 'ff6600', 'ff9900', 'ffcc00', 'ffff00', 'ccff00', '99ff00', '66ff00', '33ff00', '00ff00', '00ff33', '00ff66', '00ff99', '00ffcc', '00ffff', '00ccff', '0099ff', '0066ff', '0033ff', '0000ff', '3300ff', '6600ff', '9900ff', 'cc00ff');
var i = 0;
while (i <= text.length) {
var t = text.charAt(i);
if (t != undefined) {
texty += "<font color=\"#" + colors[i % colors.length] + "\">" + t + "</font>";
i++;
}
}
texty = texty.replace("> <", "> <");
var REPLACEME = "[rainbow]" + RAINBOWTEXT + "[/rainbow]";
txt = txt.replace(REPLACEME, texty);
document.write(txt);
}​
If we can make assumptions about no interleaving or nesting of [rainbow] tags, I'd just use a regular expression with a simple replacer callback:
var rainbowified = txt.replace(/\[rainbow\](.*?)\[\/rainbow\]/, function(textWithTags, textBetweenTheseTags) {
var text = textBetweenTheseTags;
....
for(var i = 0; i < text.length; ++i) {
// rainbowify each letter of text...
}
...
return textWithFontTags;
}
You can use this to get a new string with the transformation you want.
Also, the font tag is depricated; you should use span with color:#XXXXXX in the style attribute.
var colors = [
'f0f', 'f0c', 'f09', 'f06', 'f03', 'f00', 'f30', 'f60', 'f90', 'fc0',
'ff0', 'cf0', '9f0', '6f0', '3f0', '0f0', '0f3', '0f6', '0f9', '0fc',
'0ff', '0cf', '09f', '06f', '03f', '00f', '30f', '60f', '90f', 'c0f'
];
function rainbowify(text) {
return text.replace(/\[rainbow\](.*)\[\/rainbow\]/g, function(_, inner){
return inner.replace(/./g, function(ch, i){
return '<span style="color:#' + colors[i % colors.length] + ';">' + ch + '</span>';
});
})
}
Here's how I'd do it.
Make it a loop. .indexOf can take a starting point as the second parameter, so with starting the next iteration at lastChar+10 should work.
Apart from that, it might be easier to do it fully with regex and .replace:
return txt.replace(/\[rainbow\](.+?)\[\/rainbow\]/g, function(all, match, index, str) {
return createRGBrainbowArray(match.length).map(function(color, i) {
return '<span style="color:#'+color+'">'+match[i]+'</span>';
}).join("");
});
function createRGBrainbowArray(l) {
// should return an Array of length l with hexadecimal color strings,
// representing a nice rainbow
}

Categories

Resources