My question is that I have an html code <p> Hello World </p>
And want to change the css of every letter using JavaScript. Essentially, I will change the background color to make an animation. Is there a way to do this without making a span or some sort of tag around every letter and going through all that struggle?
I have my string array with colors and a method to call the correct color (data-index attribute).
Thanks!
EDIT: I have the entire word changing color and thought of an idea by making a function that iterates over the indexes of the innerHTML string and assigns a data-index to the letter's span by editing the function provided below by Cymen. Is this a good approach?
No, you will need to use a tag that supports background-color. You can easily wrap a string of characters in spans like so:
function wrapInSpans(string) {
return '<span>' + string.split('').join('</span><span>') + '</span>';
}
You would have to use a JavaScript function to wrap each character in a <span>.
window.onload = function() { // when everything loads, run the function
var elem = document.getElementById( "someId" );
var text = elem.innerHTML; // get the <p>'s text content
elem.innerHTML = ""; // then make the <p> empty
for( var i=0; i<text.length; i++ ) { // for each character in the text
elem.innerHTML += "<span>"+text[i]+"</span>";
}
};
Remember to change "someId" to the id of your <p> element.
You can access each individual character inside the for loop with text[i].
This would take quite a bit of code to spell out completely, but, if it's very important in your case to not actually add some type of wrapping element, then I believe this would be possible via a dynamically generated background image.
Roughly the steps would be:
Create a Range with a start and end around each character in the .textContents of the element you care about.
.getBoundingClientRect() on each range to get its rendered dimensions.
Draw rectangles of the desired color to a <canvas>.
Export the <canvas> as a data URI.
Use the data URI as a background-image.
Repeat for each block displayed element that you care about.
Be advised that there will, no doubt, be various edge cases in this approach and possible browser support limitations. Obviously just wrapping each character is a much simpler.
Related
This is quite a challenging problem. I haven't seen it solved anywhere on Stack Overflow. So I decided to post it.
0 ----17----+ +---30---
| | | +----47
| | | |
<div>ABC<b>B Elem<i>Italic</i>ent</b> DEF</div>
| |
+---8--- ---37--+
Action: Let's say Element <i> tag is clicked.
Problem: Create a function that returns coordinates [17,30]
Note: The coordinates are start and end caret position, represented as 0-based index, in original HTML source code, encompassing only the element that was clicked. May assume normalized HTML nodes as in id = "" becomes id="". (But extra credit, if it doesn't.)
Example 2: If <b> tag was clicked. The script should return [8, 37] because it is the start/end caret position encompassing the B tag.
Example 3: If ABC text or DEF text was clicked, return value is [0,47]
Walk the parent chain until you hit whatever tag you consider to be a container (<div> in your case, apparently).
Use the parent's childs to locate the particular child you're coming from, in case you have two or more identical childs, like in from <i>two</i> to <i>two</i> to <i>two</i> <i>two</i>.
That should give you the child offset within the parent. You can then cumulate the offsets until you hit the div tag or whatever other container element.
Ending position is just this offset plus the clicked element length.
And after two days of solving this, I am posting my own solution.
I tried to parse the DOM and count characters manually, at first. But that was more complicated than it had to be.
Credit: Thanks to kuroi neko, who suggested the end caret position is just start position + length of the HTML encompassing the clicked tag.
Note: I am manually removing <tbody> tags, before calculating caret values. This is because, even original HTML does not contain them, during normalization process (which takes place during innerHTML or outerHTML call,) they are auto-inserted. It's a personal preference, if you're building a text editor that needs this functionality -- to leave them alone and update original HTML.
On the other hand, if you prefer the purist approach, and want to consider the original HTML intact, as it was written by the author of said HTML, then you may want to remove <tbody> manually. This also assumes that you take responsibility for taking care of all other cases, similar to these. Whatever they might be. (Not included in the solution below.)
Solution: Considering textarea (HTML source editor) and #preview are two separate elements representing the same HTML.
$(document).ready(function() {
// Normalize source code
var normalized_html = document.getElementById("preview").innerHTML;
// Remove all TBODY tags (they are auto-inserted, even if not present in original HTML)
normalized_html = normalized_html.replace(/<tbody>/g, '');
$("#textarea").html(normalized_html);
$("#preview").on("click", function(event) {
// Get clicked tag HTML
var tag = event.target.outerHTML;
// Get original HTML before split character is inserted
var orig_html = document.getElementById("preview").innerHTML;//.replace(/<preview>/g, '').replace(/<\/preview>/g, '');
// Insert unique separator just before the tag that was clicked, to mark beginning
$(event.target).before("[*-*]");
// Get preview source code
var html = document.getElementById("preview").innerHTML;
// Remove line breaks
html = html.replace(/\r|\n/g, '');
// Remove tags that were auto-inserted by native normalization process that did not exist in original HTML.
html = html.replace(/<tbody>/g, '');
var before_split = html;
// Split HTML at the tag that was clicked
html = html.split("[*-*]")[0];
// Restore preview to original HTML
$("#preview")[0].innerHTML = orig_html;
// Get start and end of caret in source code
var caret_start = html.length;
var caret_end = caret_start + tag.length;
console.log("caret start = " + caret_start + " end = " + caret_end);
});
});
You achieve that by simply using Descop library.
// Get the source html code of target document
var html = yourFunctionToGetHTML();
// Get the target document itself
var dom = yourFunctionToGetDocument();
// Get the element you want to found in source code
var element = document.getElementById("target-element");
// Create an instance of Descop
var descop = new Descop();
// Connect document
descop.connectDocument(dom);
// Connect source code
descop.connectSource(html);
// Get element position in source code
var position = descop.getElementPosition(element);
// eg. position => { start: 320, end: 480 }
Hi i am trying to convert white space into
I have a tag in my html code <pre></pre> and contain blank space inside. I am originally trying to select the javascript value but maintaining the blank position. However i am awared that innerHTML does remove excess white space. Is there a way for me to keep the blank position. I am also trying to grab it before i do the innerHTML to do something of this sort
<script>
$(document).ready(function() {
var value = $("pre").val();
value = value.replace(" ", " ");
$("pre").val(value);
});
</script>
but it doesn't select the white space i guess.
Thank you for any help. I also don't have access to generate the html before it even gets display.
val method only works for form elements, for other elements you should html/text method instead.
$("pre").html(function(_, innerHTML) {
return innerHTML.replace(/\s/g, " ");
});
When a user create a message there is a multibox and this multibox is connected to a design panel which lets users change fonts, color, size etc.. When the message is submited the message will be displayed with html tags if the user have changed color, size etc on the font.
Note: I need the design panel, I know its possible to remove it but this is not the case :)
It's a Sharepoint standard, The only solution I have is to use javascript to strip these tags when it displayed. The user should only be able to insert links, images and add linebreaks.
Which means that all html tags should be stripped except <a></a>, <img> and <br> tags.
Its also important that the attributes inside the the <img> tag that wont be removed. It could be isplayed like this:
<img src="/image/Penguins.jpg" alt="Penguins.jpg" style="margin:5px;width:331px;">
How can I accomplish this with javascript?
I used to use this following codebehind C# code which worked perfectly but it would strip all html tags except <br> tag only.
public string Strip(string text)
{
return Regex.Replace(text, #"<(?!br[\x20/>])[^<>]+>", string.Empty);
}
Any kind of help is appreciated alot
Does this do what you want? http://jsfiddle.net/smerny/r7vhd/
$("body").find("*").not("a,img,br").each(function() {
$(this).replaceWith(this.innerHTML);
});
Basically select everything except a, img, br and replace them with their content.
Smerny's answer is working well except that the HTML structure is like:
var s = '<div><div>Link<span> Span</span><li></li></div></div>';
var $s = $(s);
$s.find("*").not("a,img,br").each(function() {
$(this).replaceWith(this.innerHTML);
});
console.log($s.html());
The live code is here: http://jsfiddle.net/btvuut55/1/
This happens when there are more than two wrapper outside (two divs in the example above).
Because jQuery reaches the most outside div first, and its innerHTML, which contains span has been retained.
This answer $('#container').find('*:not(br,a,img)').contents().unwrap() fails to deal with tags with empty content.
A working solution is simple: loop from the most inner element towards outside:
var $elements = $s.find("*").not("a,img,br");
for (var i = $elements.length - 1; i >= 0; i--) {
var e = $elements[i];
$(e).replaceWith(e.innerHTML);
}
The working copy is: http://jsfiddle.net/btvuut55/3/
with jQuery you can find all the elements you don't want - then use unwrap to strip the tags
$('#container').find('*:not(br,a,img)').contents().unwrap()
FIDDLE
I think it would be better to extract to good tags. It is easy to match a few tags than to remove the rest of the element and all html possibilities. Try something like this, I tested it and it works fine:
// the following regex matches the good tags with attrinutes an inner content
var ptt = new RegExp("<(?:img|a|br){1}.*/?>(?:(?:.|\n)*</(?:img|a|br){1}>)?", "g");
var input = "<this string would contain the html input to clean>";
var result = "";
var match = ptt.exec(input);
while (match) {
result += match;
match = ptt.exec(input);
}
// result will contain the clean HTML with only the good tags
console.log(result);
Is there an easy way to wrap spans around arbitrary text within an html paragraph? For example, given the following original html:
<p>Here is a dandy block of text to color up</p>
<p> WHOAH another paragraph</p>
I'd like to wrap arbitrary portions of the text based on user input. So one set of input might transform this into
<p>Here is a <span style="background:yellow">dandy block</span> of text to color up</p>
<p> WHOAH <span style="background:green">another paragraph</span></p>
While another set of input might create
<p>Here is a<span style="background:yellow">a dandy block</span> of text to color up</p>
<p> WHOAH <span style="background:green">another</span> paragraph</p>
This problem is related to this one and this one, however, the main difference with my goal is that I want the highlights to be permanent, not just temporary selections and I'd also like this to work within p elements rather than textareas.
If it's possible, I imagine it would look something like using jQuery
var innerText = $('p')[p_index].slice(char_start, char_end).text();
$('p')[p_index].slice(char_start, char_end).html(
"<span style=\"background:yellow\">"+
innerText +
"</span>");
This would (in theory) select the p_index paragraph, grab the range between the given indices and replace it with a newly created span which has the original text nested inside of it. This clearly doesn't work since subscripting on the jQuery object does not return another inner jQuery object. Though
$("p").slice(0, 1).html("<span style=\"background: blue\">" +
$("p").slice(0, 1).text() +
"</span>");
Does exactly what I want on a paragraph level, but not on the within text level. I could use this approach to do the replacement by totally writing each paragraph given the character ranges I have, but if there's an easy way, I'd greatly appreciate suggestions.
$("p")[p_index]
gives you the actual DOM element that is that paragraph at p_index, so to get the contents of the paragraph you'd need to use:
$("p")[p_index].innerHTML
// OR
$("p")[p_index].textContent
Using jQuery would be easier though. You wouldn't use the jQuery slice() method to reduce the range to a single element, you'd use the .eq() method. Try something like this:
$('p').eq(p_index).html(function(i,currentText) {
return currentText.substring(0, char_start) +
"<span style=\"background:yellow\">" +
currentText.substring(char_start, char_end) +
"</span>" +
currentText.substring(char_end);
});
When you pass a function to the .html() method, jQuery sets the html to whatever you return from the function. jQuery passes the function the current (inner) html of the element so you can process it. (If you do this on a jQuery object containing more than one element your function is called once for each element so they can be processed individually.)
Demo: http://jsfiddle.net/62HHk/
I've used this plugin in the past with nice results.
Try this:
$('input[type=text]').keyup(function() {
var val = $.trim(this.value);
var text = $('p').text().split(' ')
$.each(text, function(i, v) {
if (v == val) {
text[i] = '<span>'+v+'</span>';
}
})
$('p').html(text.join(' '))
})
Fiddle
This should work. It can easily be turned into a function that takes the word you're looking for as a parameter.
jQuery.textReplace by Ben Alman
$('.text').replaceText( /hello/g, '<span classs="interesting">hello</span>' );
I was wondering if there is a way I could find the position of letters in HTML using Javascript or jQuery? I highly doubt this is possible, but it would make my life so much easier.
Alternatively, Is there a way to find the position of <br /> tags in HTML using JS?
Thanks in advance.
As tdammers mentions, handling all of the details of putting together a process to handle what you're suggesting has many nuances that may have to be addressed, depending on what you're doing.
The basics of what you're trying to do is:
Wrap an element around the text you want to find the position for, ie, in the case of text, probably a <span>.
Get either the $.offset() or $.position() of the element or elements you added. Whichever you choose is relevant to what you're trying to do; the first is relevant to the document, the second to the containing element.
I created a simple demo of a script to "highlight" a term typed into a textbox in several paragraphs using divs with position: absolute and top/left offsets relative to the terms found in the paragraphs (located by the <span> surrounding them).
Note, this is only a demonstration (of $.offset()); it's not production-ready code. There's a link to the live fiddle demo below the code snippets.
First, I created a function to find and create a highlight <div> for each term found.
function highlightWordPositions(word) {
var $paras = $('p'),
$spans,
_top = 0,
_left = 0;
$paras.each(function(){
var $p = $(this),
regex = new RegExp(word, 'g');
$p.html($p.text().replace(regex, '<span>' + word + '</span>'));
$spans = $p.find('span');
$spans.each(function(){
var $span = $(this),
$offset = $span.offset(),
$overlay = $('<div class="overlay"/>');
$overlay
.offset($offset)
.css({
width: $span.innerWidth(),
height: $span.innerHeight()
}).show();
$(document.body).append($overlay);
});
});
}
Then, I attached a callback to the $.keyup() event:
$('#term').keyup(function(event){
var term = this.value;
if (term == '') {
$('.overlay').remove();
return false;
} else if (term.indexOf(' ') != -1) {
this.value = term.replace(' ', '');
return false;
}
$('.overlay').remove();
highlightWordPositions(term);
});
http://jsfiddle.net/JaN75/
There is a simpler and more lightweight way (vs other answers that suggest overlays) to find the position of letters in DOM elements: use a range.
// returns { left, top, etc } in px
function getLetterPositions(myElementWithText, myTextElementIndex, myLetterPosition) {
var range = document.createRange();
range.setStart(myElementWithText.childNodes[myTextElementIndex], myLetterPosition);
range.setEnd(myElementWithText.childNodes[myTextElementIndex], myLetterPosition+1);
return range.getBoundingClientRect();
}
the element with the text holding the target letter is myElementWithText (e.g. a <div>)
the element child with the text (i.e. the textNode) is at index myTextElementIndex (in most cases, 0)
the position of the letter you are trying to find is myLetterPosition (e.g. if text is "abcd" and you want "c", this will be 2)
Assuming that you mean the position where the character is displayed on screen, in pixels:
jQuery or the DOM do not model individual characters in their object models, so you can't read a character's position directly.
The best way I can think of is to insert a dummy element right before (or after) the character, e.g. a zero-width span with a special class, and then get its position. Alternatively, you can wrap the character in such a special element, and then get the wrapper's position, width, height, etc.
It's still not trivial, because you need to scan the HTML, and you don't want to break the HTML by inserting tags where they don't belong - for example, if you want to match the character 'd', you don't want to turn <div> into <<span class="magic">d</span>iv>, as that wouldn't be well-formed HTML.
Also, inserting those dummy elements may alter the layout slightly, depending on how the browser handles kerning.
in action: http://jsfiddle.net/WKgWM/
html:
<p>Percussus ait in fuerat construeret cena reges undis effugere quod una.</p>
js:
var selection = "dis";
var spn = '<span class="selected">'+selection+'</span>';
$("p").html($("p").html().replace(selection,spn));
css:
.selected{
background-color:#FF00FF;
}