i'm trying to make some code that'll loop over html forum data and replace some brackets.
i'm basically trying to turn
<br><br>
<br>
<img src="blah"></img>
to
[br][br]
[br]
[img src="blah"][/img]
and avoid changing text like
:<
<("<) <(")> (>")>
I'm using javascript and a regex pattern for now. I was able to find a regex for something between two tags, but not two tags around a string. What would be the best way to do this?
There's probably a far more elegant way to do this - but this is basically how I did it in the early 2000's (slight update to more modern JS with const/let etc)
const input = `<br><br>
<br>
<img src="blah"></img>`;
const body = document.createElement('body');
body.innerHTML = input;
function square(obj) {
let out = '';
let el;
while(el = obj.firstChild) {
if (el.nodeType == 3) {
out += el.nodeValue;
} else {
out += `[${el.nodeName}]`;
out += square(el);
if (!["BR"].includes(el.nodeName)) {
out += `[/${el.nodeName}]`;
}
}
el.remove();
}
return out;
}
console.log(square(body));
I need to change the color of a specific sub-string in a text.
The text looks like this:
SOME TE<br>
XT IS HER<br>
E.
I tired the .replace() function from jquery, the problem with it is, that, as you can see above, the text is splited with those <br>'s. How can I "ignore" them ?
For example I want to replace the String TEXT with <span class="color-red">TEXT</span>
Does anyone has an idea on how to solve this problem ?
No jQuery required. Although I did not test this at all so it might need tweaking.
var customReplace = function(str, subStr, color) {
var words = str.split(' ');
for (var i = words.length - 1; i >= 0; i--) {
var word = words[i].replace(/<br>/g, '');
if (word === subStr) {
words[i] = '<span class="color-' + color + '">' + words[i] + '</span>';
}
}
return words.join('');
}
I think a good Idea could be something like following:
fine all text nodes and wrap them inside a span§
re-insert all tags
var ALL_TAGS_REGEX = /(.+)(<{1}\/{0,1}.+>{1})/g;
document.addEventListener("DOMContentLoaded", function() {
document.body.innerHTML = document
.body
.innerHTML
.replace(ALL_TAGS_REGEX, '<span class="highlight">$1</span>$2')
;
});
.highlight { background: cyan; }
I am a string,<br>
<div> something else but no text node</div>
Other text nodes at libitum
< br > tag is break in line, color won't be applicable on < br > text, this would not accounted as normal text while final html render
Firstly, I've made a CODEPEN or jsfiddles
Background:
Ok I have a span tag within a few header tags h1,h2,h3. Inside that spantag is the word
experience which is spelled backwards like so:
<h3>for <span class="hover-spell">ecneirepxe</span>.</h3>
Question
I'm unsure on the best way to approch this but I would like on hover:
reorder to spell experience correctly
if possible animate them overlapping another while re-ordering
I have no idea how to do this but I keep thinking regex, with arrays but this feels overly complicated and I really don't know anything about regex and proper array sorting. Any information to lead me in the right direction would be most appreciated. Or an edit to the codepen or jsfiddles would be so excellent.
One possible solution is to use css to accomplish this. This solution doesn't animate the transition, it just changes the order of the letters. Add this to your css:
.hover-spell:hover{
direction: rtl;
unicode-bidi: bidi-override;
}
Edit: Thanks to Marcel Gwerder for pointing out that it's not possible to animate the direction property
I found this answer, in another post (it goes through a given string of text and wraps each character in a span then assigns transiton styles to each), that may help with a jquery solution.
I've just tried to set up something animated with jquery, it's a bit tricky to get a fancy looking animation. But that one doesn't look too bad (DEMO).
var expElem = $(".hover-spell");
var exp = expElem.text();
var run = false;
expElem.empty();
for(var i = 0; i <= exp.length; i++) {
expElem.append('<span>'+exp.charAt(i)+'</span>');
}
expElem.mouseover(function() {
if(run === true) return false;
run = true;
var stepDuration = 300;
var counter = 0;
(function anim(){
if(counter == exp.length -1) return false; //Remove -1 to get last "e" animated
counter++;
var nth = exp.length;
var elem = $('span:nth-child('+nth+')', expElem);
elem.slideUp(stepDuration, function() {
(function() {
if(counter == 1) return elem.prependTo(expElem);
else return elem.insertAfter($('span:nth-child('+(counter-1)+')', expElem));
})().slideDown(stepDuration, anim);
});
})();
});
To get it working with hover(including mouseleave) is a bit more complicated. You could also try something with storing the position and then slide them over each other but again a bit more complicated.
<span id = "spell">olleh</span> //hello in reverse
<script type="text/javascript">
var newText;
var text = null;
text = document.getElementById("spell").innerHTML;
for (var i = text.length - 1; i >= 0; i--) {
if (i == text.length - 1) {
newText = text.substr(i, 1);
}
else {
newText = newText + text.substr(i, 1);
}
}
alert(newText);
</script>
write this script in body tag...
I have been struggling with this for a few days. I need somebody to steer me in the right direction. I have been searching on the web. I am not sure if I took the right approach. What I need is that each time a person hovers over a particular keyword, it should display an alert box. In this example the word is else. When I run the code it does not give any errors and does not display anything when mouse hovers on the word.
function on_func2()
{
var searchString = 'else';
var elements = document.getElementById('paragraph2');
for (var i = 0; i < elements.length; i++)
{
if (elements[i].innerHTML.indexOf(searchString) !== -1)
{
alert('Match');
break;
}
}
}
I would do something like this:
It will go through and find all else words, and wrap them in a span with a listener bound:
<p id="hello">What else would you say?</p>
-
var hello = document.getElementById('hello');
var str = hello.innerHTML;
str = str.replace( /\b(else)\b/g, '<span onmouseover="func1()">$1</span>' );
hello.innerHTML = str;
function func1() {
alert('there');
}
Check out the fiddle.
Using jQuery lettering plugin
<p class="word_split">if you were not there, else I would not have won.<p>
$(".word_split").lettering('words');
$('.word_split').mouseover(function(event) {
var word=event.target.innerHTML;
if (word == "else")
alert("Yep");
});
here's a demo: jsFiddle
I am using a 'contenteditable' <div/> and enabling PASTE.
It is amazing the amount of markup code that gets pasted in from a clipboard copy from Microsoft Word. I am battling this, and have gotten about 1/2 way there using Prototypes' stripTags() function (which unfortunately does not seem to enable me to keep some tags).
However, even after that, I wind up with a mind-blowing amount of unneeded markup code.
So my question is, is there some function (using JavaScript), or approach I can use that will clean up the majority of this unneeded markup?
Here is the function I wound up writing that does the job fairly well (as far as I can tell anyway).
I am certainly open for improvement suggestions if anyone has any. Thanks.
function cleanWordPaste( in_word_text ) {
var tmp = document.createElement("DIV");
tmp.innerHTML = in_word_text;
var newString = tmp.textContent||tmp.innerText;
// this next piece converts line breaks into break tags
// and removes the seemingly endless crap code
newString = newString.replace(/\n\n/g, "<br />").replace(/.*<!--.*-->/g,"");
// this next piece removes any break tags (up to 10) at beginning
for ( i=0; i<10; i++ ) {
if ( newString.substr(0,6)=="<br />" ) {
newString = newString.replace("<br />", "");
}
}
return newString;
}
Hope this is helpful to some of you.
You can either use the full CKEditor which cleans on paste, or look at the source.
I am using this:
$(body_doc).find('body').bind('paste',function(e){
var rte = $(this);
_activeRTEData = $(rte).html();
beginLen = $.trim($(rte).html()).length;
setTimeout(function(){
var text = $(rte).html();
var newLen = $.trim(text).length;
//identify the first char that changed to determine caret location
caret = 0;
for(i=0;i < newLen; i++){
if(_activeRTEData[i] != text[i]){
caret = i-1;
break;
}
}
var origText = text.slice(0,caret);
var newText = text.slice(caret, newLen - beginLen + caret + 4);
var tailText = text.slice(newLen - beginLen + caret + 4, newLen);
var newText = newText.replace(/(.*(?:endif-->))|([ ]?<[^>]*>[ ]?)|( )|([^}]*})/g,'');
newText = newText.replace(/[·]/g,'');
$(rte).html(origText + newText + tailText);
$(rte).contents().last().focus();
},100);
});
body_doc is the editable iframe, if you are using an editable div you could drop out the .find('body') part. Basically it detects a paste event, checks the location cleans the new text and then places the cleaned text back where it was pasted. (Sounds confusing... but it's not really as bad as it sounds.
The setTimeout is needed because you can't grab the text until it is actually pasted into the element, paste events fire as soon as the paste begins.
How about having a "paste as plain text" button which displays a <textarea>, allowing the user to paste the text in there? that way, all tags will be stripped for you. That's what I do with my CMS; I gave up trying to clean up Word's mess.
You can do it with regex
Remove head tag
Remove script tags
Remove styles tag
let clipboardData = event.clipboardData || window.clipboardData;
let pastedText = clipboardData.getData('text/html');
pastedText = pastedText.replace(/\<head[^>]*\>([^]*)\<\/head/g, '');
pastedText = pastedText.replace(/\<script[^>]*\>([^]*)\<\/script/g, '');
pastedText = pastedText.replace(/\<style[^>]*\>([^]*)\<\/style/g, '');
// pastedText = pastedText.replace(/<(?!(\/\s*)?(b|i|u)[>,\s])([^>])*>/g, '');
here the sample : https://stackblitz.com/edit/angular-u9vprc
I did something like that long ago, where i totally cleaned up the stuff in a rich text editor and converted font tags to styles, brs to p's, etc, to keep it consistant between browsers and prevent certain ugly things from getting in via paste. I took my recursive function and ripped out most of it except for the core logic, this might be a good starting point ("result" is an object that accumulates the result, which probably takes a second pass to convert to a string), if that is what you need:
var cleanDom = function(result, n) {
var nn = n.nodeName;
if(nn=="#text") {
var text = n.nodeValue;
}
else {
if(nn=="A" && n.href)
...;
else if(nn=="IMG" & n.src) {
....
}
else if(nn=="DIV") {
if(n.className=="indent")
...
}
else if(nn=="FONT") {
}
else if(nn=="BR") {
}
if(!UNSUPPORTED_ELEMENTS[nn]) {
if(n.childNodes.length > 0)
for(var i=0; i<n.childNodes.length; i++)
cleanDom(result, n.childNodes[i]);
}
}
}
This works great to remove any comments from HTML text, including those from Word:
function CleanWordPastedHTML(sTextHTML) {
var sStartComment = "<!--", sEndComment = "-->";
while (true) {
var iStart = sTextHTML.indexOf(sStartComment);
if (iStart == -1) break;
var iEnd = sTextHTML.indexOf(sEndComment, iStart);
if (iEnd == -1) break;
sTextHTML = sTextHTML.substring(0, iStart) + sTextHTML.substring(iEnd + sEndComment.length);
}
return sTextHTML;
}
Had a similar issue with line-breaks being counted as characters and I had to remove them.
$(document).ready(function(){
$(".section-overview textarea").bind({
paste : function(){
setTimeout(function(){
//textarea
var text = $(".section-overview textarea").val();
// look for any "\n" occurences and replace them
var newString = text.replace(/\n/g, '');
// print new string
$(".section-overview textarea").val(newString);
},100);
}
});
});
Could you paste to a hidden textarea, copy from same textarea, and paste to your target?
Hate to say it, but I eventually gave up making TinyMCE handle Word crap the way I want. Now I just have an email sent to me every time a user's input contains certain HTML (look for <span lang="en-US"> for example) and I correct it manually.