Javascript match regex - prevent greediness - javascript

I am having an issue with my javascript match() regex.
<div class="a"> whitespace, new lines, and content </div>
<div class="junk"> junkjunkjunk </div>
<div class="a"> whitespace, new lines, and content </div>
<div class="junk"> junkjunkjunk </div>
<div class="a"> whitespace, new lines, and content </div>
Let's say I want to capture everything in between <div class="a"> and the closest </div>. The following regex is capturing everything, I'm assuming due to greediness:
/<div class="a">[\s\S]+<\/div>?/ig
I want to capture each <div class="a">...</div> individually such that I can output each as capture[0], capture[1], etc. How would I do this?
Thank you.
EDIT: Updated to better reflect my problem. Assume there is undesired markup and text between desired divs.

First, parsing HTML with regex is baaad... seriously man, you can use the innerHTML property of each div to change it's content, or better, use jQuery or another javascript framework to do this kind of jobs.
This job can be made with jquery in this way:
$("div.a").each(
function() {
alert($(this).html())
}
);
Second, if you want badly to use regex, and assuming there is only text (no markup) between the divs, you can use something like this:
/<div class="a">([^<])+<\/div>/ig

To give a straight regex answer:
To remove the greediness of the quantifiers, put a ? after the quantifier like this:
/<div class="a">[\s\S]+?<\/div>?/ig
This forces the + to match as less as possible. Works also with the *.

then you need the question mark before the closing div but after the + operator, and use () around what you want to capture.

One way to prevent regex greediness, is to not use regex.
If you'll allow for an alternate solution. This assumes your HTML is in string form, and not part of the DOM:
var str = '<div class="a"> whitespace, new lines, and content </div>\
<div class="a"> whitespace, new lines, and content </div>\
<div class="a"> whitespace, new lines, and content </div>';
var temp = document.createElement('div');
temp.innerHTML = str;
var capture = [];
for( var i = 0; i < temp.childNodes.length; i++ ) {
var node = temp.childNodes[i];
if( node && node.nodeType === 1 && node.className === 'a' ) {
capture.push( node.innerHTML );
}
}
alert(capture[0]);
With respect to a regex, here's one approach using .replace():
var str = '<div class="a"> whitespace, new lines, and content </div>\
<div class="a"> whitespace, new lines, and content </div>\
<div class="a"> whitespace, new lines, and content </div>';
var res = [];
str.replace(/<div class="a">([^<]+)<\/div>/ig,function(s,g1) {
res.push(g1);
});

Related

txt.replace </blockquote> in textarea js

I give up! I looked at many different answers. I've tried many different ways and nothing works. I want to change the </blackquote> tag to <br /> or a new line in the textarea. Alternatively, change to some other character, because later I can replace another character in PHP to <br/>. How to do it?
Working example for easy understand here: https://jsfiddle.net/jsf88/rb3xp7am/35/
<textarea id="comment" name="quote" placeholder="quote" style="width:80%;height:200px;"></textarea>
<section class="replyBox" style="width: 100%;"><br/>
[ click for quote ]
<div class="replyMsg">
<blockquote>this is a quote for comment😎 </blockquote><br />
"X" -- HERE I want BR_TAG or new line in textarea after click 'quote' 😐
</div>
</section>
$(document).on('ready', function() {
$('.quoteMsg').click(function() {
var txt = $(this).closest('.replyBox').find('.replyMsg').text();
//txt = txt.replace('</blockquote>', '<br/>');
//txt = txt.replace(/<\/(blockquote)\>/g, "<br/>");
//txt = txt.replace(/blockquote*/g, '<br/>');
//txt = txt.replace(/(.*?)<\/blockquote>(.*?)/g, ' xxx ');
txt = txt.replace(/<\/blockquote>/gi, '<br/>')//NOT WORKING!!
txt = txt.replace(/(?:\r\n|\r|\n)/g, ' ');//working great
console.log(txt);
$("textarea[name='quote']").val($.trim('[quote]' + txt + '[/quote]'));
});
});
To make it funnier, another example with changing the blackquote tag to br works without a problem. Why? can someone explain it?
//OTHER EXAMPLES WHERE CHANGE </BLACKQUOTE> to <br/> WORKING GOOD... WTF?!
string = ` <blockquote>this is a quote for comment😎 </blockquote><br />"X" -- HERE I want BR_TAG or new line in textarea after click 'quote' 😐`;
string = string
.replace(/<\/blockquote>/gi, ' <br /> ');//but here working! ;/
console.log(string);
you recover text with text function ('.replyMsg').text() but in that case you will have the text but with no html tag like <blockquote> so first you will have to recover the html to have the blockquote tag
var txt = $(this).closest('.replyBox').find('.replyMsg').html();
the br tag is not interpreted in textarea so you have to change it by a new line character
don't forget to remove opened bloquote tag to get the expected result
txt = txt.replace(/<blockquote>/gi, '');
$('.quoteMsg').click(function() {
var txt = $(this).closest('.replyBox').find('.replyMsg').html();
txt = txt.replace(/(?:\r\n|\r|\n)/g, ' ');
txt = txt.replace(/<\/blockquote>/gi, '\n');
txt = txt.replace(/<blockquote>/gi, '');
console.log(txt);
$("textarea[name='quote']").val($.trim('[quote]' + txt + '[/quote]'));
});
blockquote {
background-color: silver;
}
.replyMsg {
border: 2px solid green;
}
.quoteMsg {
background-color: green;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea id="comment" name="quote" placeholder="quote" style="width:80%;height:200px;"></textarea>
<section class="replyBox" style="width: 100%;"><br/>
[ click for quote ]
<div class="replyMsg">
<blockquote>this is a quote for comment😎 </blockquote>
"X" -- HERE I want BR_TAG or new line in textare a after c lick 'quote' 😐
</div>
</section>
The first problem in your code was how you were adding the event listener to the ready event. Being it something invented by jQuery, and not a native event, the correct way to do it should be as of now (v.3.3.1 the version I used in this demo) $(document).ready(()=>{/*code here*/}).
As a further reference:
https://api.jquery.com/ready/
There is also $(document).on( "ready", handler ), deprecated as of
jQuery 1.8 and removed in jQuery 3.0. Note that if the DOM becomes
ready before this event is attached, the handler will not be executed.
But... it's not perfectly clear how did you wish to transform your text before setting the value of the textarea. So I just better factored your logic so that you have some clear steps:
grabbing the blockquote element text content and trimming it (being the origin)
applying the transform newline to whitespace (with the regex that I left untouched)
build the final string as a template literal that will include the quote content, the meta tags wrapping it, AND anything else you wish to add like for example a new line (\n) that in this example is exacerbated by a text following it.
There's a hint in your words that put me in the position to say something superflous but still deserving an attempt: the value of a inner text is just plain text and doesn't render html content. So the <br> itself would remain as you read it and wouldn't have any rendering effect on the textarea content. That's why I focused my demonstration on putting a newline with the escaping sequence. It works both on double quoted strings and template literals: "\n" `\n`
Further notes
It seems the original approach of processing the blockquote html was preferred. It's worth saying that it was appearently a terrible strategy for several reasons:
It grabs the blockquote content as html despite that's not how it's
rendered on the page.
It takes the effort to consider the whole outerHTML removing the
wrapping blockquote tags instead of fetching directly the innerHTML.
It adds the newline as newline instead of embedding it as <br> so
at this point I ask myself if the content in the textarea was
supposed to be encoded html or not.. and the added br would then
belong to something meta?
It's harder to deal with in case you want to further customize the
string processing
But... maybe there's something I didn't get and I'm doing weak assumptions.
//since you are using the ready event with jquery, that's the correct syntax
$(document).ready(function() {
$('.quoteMsg').click(function() {
//grabs the text content of the blockquote element (trimming it)
var quoteTextContent = $(this).closest('.replyBox').find('.replyMsg').text().trim();
//performs the transform already in place in your code.. replacing newlines with white spaces
quoteTextContent = quoteTextContent.replace(/(?:\r\n|\r|\n)/g, ' '); //working great
//builds the string to set the textarea value with, using a template literal
//here you can add anything you want.. like a new line but that's just an example
const encoded = `[quote]${quoteTextContent}[/quote]\nand something following to show the new line happening`;
console.log(encoded);
$("textarea[name='quote']").val( encoded );
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea id="comment" name="quote" placeholder="quote" style="width:80%;height:200px;"></textarea>
<section class="replyBox" style="width: 100%;"><br/>
[ click for quote ]
<div class="replyMsg">
<blockquote>this is a quote for comment😎
<br>
Having new lines also ... since you perform a regex transform newline=>whitespace
</blockquote><br />
</div>
</section>
Well, thanks for answers. The problem was a missing .html tag.
This script work for me almost perfect for quoting few times:
$(document).on('ready', function() {
$('.quoteMsg').click(function() {
var txt = $(this).closest('.replyBox').find('.replyMsg').html();
txt = txt.replace(/(?:\r\n|\r|\n)/g, ' ');
txt = txt.replace(/</g, "<");
txt = txt.replace(/>/g, ">");
txt = txt.replace(/&/g, "&");
txt = txt.replace(/"/g, '"');
txt = txt.replace(/'/g, "'");
txt = txt.replace(/<br>/g, "");
txt = txt.replace(/<hr>/g, "[hr]");
//txt = txt.replace(/<hr>/g, "\n");
txt = txt.replace(/<blockquote>/gi, '');
txt = txt.replace(/<\/blockquote>/gi, '[hr]');
txt = txt.replace(/[hr][hr]/gi, "");//not working ([][])
txt = txt.replace(/[hr][hr]/gi, "[hr]");//not working ([[hr]][[hr]])
console.log(txt);
$("textarea[name='quote']").val($.trim('[quote]' + txt + '[/quote]\n'));
});
});
The problem here is I dont know how to change dubble [hr][hr] for nothing, because this txt = txt.replace(/[hr][hr]/g, ""); not working, so would be cool for more explain about. One more time big thanks for answers! this function .replace is not as intuitive as in PHP.
EDIT: ahh.. I think is not possible to delete this dubel, because I extra insert it two times. Nvm. I will find and del this dubel in PHP.

How to selectively replace text with HTML?

I have an application where users can write comments containing HTML code, which is escaped before displaying:
<div class="card-body">
<p class="card-text">
<h1>HOLA</h1> Cita:#2<h1>HOLA</h1> <h1>HOLA</h1> Cita:#6<h1>HOLA</h1> <h1>HOLA</h1>
</p>
</div>
But when a user write a specific word like "Cita:#1" I want to transform it with jQuery to a link, so later I can load an Ajax popup there with this code:
$('.card-text').each(function() {
$(this).html($(this).text().replace(/Cita:#(\d+)/ig, 'Cita:#$1'));
});
My problem is that it does it well but also transform all possible HTML tags inside that comment too.
Is there a way to just ignore all tags that can be inside the comment and only replace the word "Cita:#1" with a link and made it works?
Actual:
Expected:
Since you control the server-side here, this would be much easier to do in PHP:
$string = '<h1>HOLA</h1> Cita:#2<h1>HOLA</h1> <h1>HOLA</h1> Cita:#6<h1>HOLA</h1> <h1>HOLA</h1>';
$string = htmlspecialchars($string);
$string = preg_replace(
'/Cita:#(\\d+)/i',
'Cita:#$1',
$string
);
echo $string;
Output:
<h1>HOLA</h1> Cita:#2<h1>HOLA</h1> <h1>HOLA</h1> Cita:#6<h1>HOLA</h1> <h1>HOLA</h1>
What you need to do is separate the matching parts of the text and process them as HTML strings, but escape* the rest of the text. Since regular expression matches provide the index of the match, this is easy enough to do.
$('.card-text').each(function() {
var before = "";
var after = $(this).text();
var link = "";
// look for each match in the string
while (match = after.match(/\b(Cita:#(\d+))\b/i)) {
// isolate the portion before the match and escape it, this will become the output
before += $("<div>").text(after.substring(0, match.index)).html();
// isolate the portion after the match, for use in the next loop
after = after.substring(match.index + match[1].length);
// build a link and append it to our eventual output
before += match[1].replace(/Cita:#(\d+)/i, 'Cita:#$1');
}
// deal with the final bit of the string, which needs to be escaped
this.innerHTML = before + $("<div>").text(after).html();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="card-body">
<p class="card-text">
<span class="foo">Here is some text. <b>Cita:#1</b> and <b>Cita:#2</b></span>
</p>
</div>
*We use jQuery to build an element, pass the string as the text content of that element, and finally get the HTML output:
htmlString = "<div>foo</div>";
console.log(
$("<div>").text(htmlString).html()
);
Output:
<div>foo</div>

Using regex with javascript on nodejs find html attribute and prepend something to its value

I have some markup in JS as follows:
<div class="col-sm-4">
<span id="some-media" class="media">Text</span>
</div>
I would like to select the class attribute of the span and prepend its value with lets say the characters: "::". So after the regex replace i would end up with:
<div class="col-sm-4">
<span id="some-media" class="::media">Text</span>
</div>
EDIT: Note that the order of the attributes in the HTML element is variable so my span attributes could very well have different order like so:
<div class="col-sm-4">
<span class="::media" id="some-media" >Text</span>
</div>
You got a regex solution, this is a DOMmy one:
var html = `<div class="col-sm-4">
<span id="some-media" class="media">Text</span>
</div>`
var doc = (new DOMParser()).parseFromString(html, "text/html");
var el = doc.getElementsByTagName('span')[0];
el.setAttribute('class', '::' + el.className);
console.log(
doc.getElementsByClassName('::media').length > 0 // check if modification's done
);
Since you have no way except Regular Expressions this can be considered as a workaround:
(<span[^>]*class=.)([^'"]+)
JS:
var html = `<div class="col-sm-4">
<span id="some-media" class="media">Text</span>
</div>
<span class="media" id="some-media">Text</span>
`;
console.log(
html.replace(/(<span[^>]*class=.)([^'"]+)/g, `$1::$2`)
);
This isn't using regex, but you can do it like this in vanilla JavaScript:
const el = document.getElementsByClassName('media')[0];
el.className = '::' + el.className;
Or in jQuery:
const $el = $('div span.media');
$el.attr('class', '::' + $el.attr('class'));
Hope this helps.
Don't parse html with regex, use DocumentFragment (or DOMParser) object instead:
var html_str = '<div class="col-sm-4"><span class="media">Text</span></div>',
df = document.createRange().createContextualFragment(html_str),
span = df.querySelector('span');
span.setAttribute('class', '::' + span.getAttribute('class'));
console.log(df.querySelector('div').outerHTML);
I think this is what you're after:
var test = $("#some-media")[0].outerHTML();
var test2 = '<div id="some-media" class="media">Text</div>'
if(/span/.test(test)) //Valid as contains 'span'
alert(test.replace(/(class=")/g, "$1::"));
if(/span/.test(test2)) //Not valid
alert(test.replace(/(class=")/g, "$1::"));
Since the order differs, writing a regex that captures all possible combinations of syntax might be rather difficult.
So we'd need a full list of rules the span follows so we can identify that span?
Got some more info about if the span occurs in a longer HTML string? Or is the string this span and this span only?
An alternative would be to use one of the several node DOM modules available, so you can work with HTML nodes and be able to use any of the above solutions to make the problem simpler.
But since you're using node:
1) Are you using any templating engines? If so, why not rerender the entire template?
2) Why does the class name have to change on the server side? Isn't there a workaround on the clientside where you do have access to the DOM natively? Or if it's just to add styling, why not add another css file that overwrites the styling of spans with className 'media'?
3) If all of the above is not applicable and it;s a trivial problem like you say, what error di you get using a simple replace?
strHTML.replace( 'class="media"', 'class="::media"' )
or if it has to be regex:
strHTML.replace( /class=\"(.*)\"/, 'class=\"::$1\"' );

Regex Replace HTML Tags

Having some problems with regex.
I have long string
str = '<div class=\"edit\">some text here
<div><br></div>
<div>then other row</div>
<div><br></div>
<div>and one more</div>
</div>
<div class=\"edit\">some text here
<div><br></div><div>then other row</div></div>'
and I want to have:
<div class\"edit\">some text here<br>then other row<br>and one more<div>
<div class=\"edit"\>some text here<br>then other row<div>
And i trying do this:
str = str.replace(/<div><br><\/div>/ig, "<br>");
str = str.replace(/<div>/ig, "<br>");
str = str.replace(/<\/div>/ig, "");
Generally speaking it is bad idea to transform html-strings using regexp. There is DOM for that. Nevertheless this one seems to work:
str = str.replace(/<div><br><\/div>/gi, '<br>')
.replace(/<div>([^<]*)<\/div>/gi, '$1');
The resulting string is "<div class="edit">some text here <br> then other row <br> and one more</div><div class="edit">some text here<br>then other row</div>"

jQuery find and replace string

I have somewhere on website a specific text, let's say "lollypops", and I want to replace all the occurrences of this string with "marshmellows". The problem is that I don't know where exactly the text is. I know I could do something like:
$(body).html($(body).html().replace('lollypops', 'marshmellows'));
This would probably work, but I need to rewrite as little HTML as I can, so I'm thinking something like:
search for the string
find the closest parent element
rewrite only the closest parent element
replace this even in attributes, but not all, for example replace it in class, but not in src
In example, I would have structure like this
<body>
<div>
<div>
<p>
<h1>
<a>lollypops</a>
</h1>
</p>
<span>lollypops</span>
</div>
</div>
<p>
<span class="lollypops">Hello, World!</span>
<img src="/lollypops.jpg" alt="Cool image" />
</p>
<body>
In this example, every occurrence of "lollypops" would be replaced, only <img src="... would remain the same and the only elements that would actually be manipulated would be <a> and both <span>s.
Does anybody know how to do this?
You could do something like this:
$("span, p").each(function() {
var text = $(this).text();
text = text.replace("lollypops", "marshmellows");
$(this).text(text);
});
It will be better to mark all tags with text that needs to be examined with a suitable class name.
Also, this may have performance issues. jQuery or javascript in general aren't really suitable for this kind of operations. You are better off doing it server side.
You could do something this way:
$(document.body).find('*').each(function() {
if($(this).hasClass('lollypops')){ //class replacing..many ways to do this :)
$(this).removeClass('lollypops');
$(this).addClass('marshmellows');
}
var tmp = $(this).children().remove(); //removing and saving children to a tmp obj
var text = $(this).text(); //getting just current node text
text = text.replace(/lollypops/g, "marshmellows"); //replacing every lollypops occurence with marshmellows
$(this).text(text); //setting text
$(this).append(tmp); //re-append 'foundlings'
});
example: http://jsfiddle.net/steweb/MhQZD/
You could do something like this:
HTML
<div class="element">
<span>Hi, I am Murtaza</span>
</div>
jQuery
$(".element span").text(function(index, text) {
return text.replace('am', 'am not');
});
Below is the code I used to replace some text, with colored text. It's simple, took the text and replace it within an HTML tag. It works for each words in that class tags.
$('.hightlight').each(function(){
//highlight_words('going', this);
var high = 'going';
high = high.replace(/\W/g, '');
var str = high.split(" ");
var text = $(this).text();
text = text.replace(str, "<span style='color: blue'>"+str+"</span>");
$(this).html(text);
});
var string ='my string'
var new_string = string.replace('string','new string');
alert(string);
alert(new_string);
Why you just don't add a class to the string container and then replace the inner text ? Just like in this example.
HTML:
<div>
<div>
<p>
<h1>
<a class="swapText">lollipops</a>
</h1>
</p>
<span class="swapText">lollipops</span>
</div>
</div>
<p>
<span class="lollipops">Hello, World!</span>
<img src="/lollipops.jpg" alt="Cool image" />
</p>
jQuery:
$(document).ready(function() {
$('.swapText').text("marshmallows");
});

Categories

Resources