Regex match group based on common groupings - javascript

Good evening,
I'm trying to group the below text so it will allow me to replace with a <ol>$1</ol>. Seems simple enough but I can't master the greediness.
Sample text (I'm aware its a repeated list, I copy+pasted for an example to show separation):
<h4>Properties</h4>
<dl><dd> Oxygen is gas at standard temperature and pressure.</dd></dl>
<h4>Testing for Oxygen</h4>
<li>A1</li>
<li>A2</li>
<li>A3</li>
<h4>Other Properties</h4>
<dl><dd> Oxygen is gas at standard temperature and pressure.</dd></dl>
<h4>Testing for Something Else</h4>
<li>B1</li>
<li>B2</li>
<li>B3</li>
The last entry can be either end-of-string or preceding another '\n'
I would like the 2 matches to be
<li>A1</li>
<li>A2</li>
<li>A3</li>
and
<li>B1</li>
<li>B2</li>
<li>B3</li>
So I can wrap in some <ol>...</ol>, simple enough. [There are other un-order lists on the page so using a temporary <lio> tag in my actual code then replacing with <li> at the end of the function].
I've gotten to the point of the below but its too greedy and starting at the first (A1's) <li> and stopping at the last (B3's) </li>
Current match:
<li>A1</li>
<li>A2</li>
<li>A3</li>
<h4>Other Properties</h4>
<dl><dd> Oxygen is gas at standard temperature and pressure.</dd></dl>
<h4>Testing for Something Else</h4>
<li>B1</li>
<li>B2</li>
<li>B3</li>
Current search string: (?!\<\/li\>.)*(\<li\>[^¬]+(\<\/li\>))(?!\<\/li\>) which captures the entire thing on Full Match (groups are a bit squiffy)
Doing this in JavaScript.
Any advice?
Thank you,

Got it licked
This does it:
(\<lio\>[\w\W]*?)\n+(\<[^l]|\0|\w|¬)

Related

use querySelectorAll() to select second element of the list

Recently I am starting to learn HTML and JS. When I am leaning the document.querySelectorAll() API, I saw it can use
document.querySelectorAll('#example-container li:first-child');
to select the first child of the list which has id name is example-container.
So I thought may be
document.querySelectorAll('#example-container li:second-child');
can select the second child of the list which has id name is example-container.
But obviously it is wrong. So I am confused how can I access the second or third item of the list by using querySelectorAll()?
I post the HTML code below:
<div id="example-container">
<ul>
<li class="feature">Luxurious sized master suite</li>
<li class="feature">Oversized walk-in closet</li>
<li class="feature">Frameless Beech cabinetry with concealed hinges</li>
<li class="feature">Elegant slab white quartz countertops with large backsplash</li>
<li class="feature">Dual china sinks with Moen faucets</li>
<li class="feature">Clear frameless shower enclosures</li>
</ul>
Since you are asking what you can do to select the second or third element using query selectors, like document.querySelectorAll('#example-container li:second-child'), I am going to tell you how to select them with both css selectors and document.querySelectorAll().
You could use:
const el = document.querySelectorAll('#example-container li')[1];
to select the second element in the list. And this is probably the preferred way to do it in JavaScript.
But css has something called :nth-child() which allows you to choose a specific child in the list. In JS you could do something like:
const el = document.querySelector('#example-container li:nth-child(2)');
to select the second list item. Notice that you do not need the querySelectorAll() method.
I hope that helps a bit.
document.querySelectorAll('#example-container li') return a collection with all li nodes in example-container.
Its like array, so you can iterate it.
document.querySelectorAll('#example-container li')[index]
console.log(document.querySelectorAll('#example-container li')[1])
//its second
<div id="example-container">
<img src="img/coded-homes-logo-sm.png"
class="img-responsive" />
<img src="img/home.jpg"
class="thumbnail img-responsive push-down-top" />
<section class="description">
<p class="h5">Five bedrooms, three full baths and 3,702 square feet of living space.</p>
<p>Extra-tall double-door entry opens to an inviting and spacious foyer. Formal living and dining rooms featuring see through fireplace.</p>
<p>Large gourmet kitchen with granite countertops, stainless steel appliances with double ovens. Arrow shaped center island, huge, walk in pantry and lots of cabinets for storage. Large eating area off the kitchen for the family to enjoy meals together.</p>
<p>The front bedroom with full bathroom just outside the door. Huge family room with recessed entertainment area complete with recessed lighting and ceiling fan. The huge master suite features a soaker tub, large shower, walk-in closet and an additional over-sized walk-in closet. The master bath features split sinks with granite countertops. 3 large, secondary bedrooms and large bathroom that is great for kids to share.</p>
<p>Also includes: 3 car garage, high ceilings throughout, wired for spa in backyard, dual pane windows, new tile and carpet on bottom floor and large, upstairs laundry room. Nicely landscaped backyard with the best views in the Temescal Valley.</p>
</section>
<h2 class="h4">Features</h2>
<ul>
<li class="feature">First</li>
<li class="feature">Second</li>
<li class="feature">Frameless Beech cabinetry with concealed hinges</li>
<li class="feature">Elegant slab white quartz countertops with large backsplash</li>
<li class="feature">Dual china sinks with Moen faucets</li>
<li class="feature">Clear frameless shower enclosures</li>
</ul>

How can I use Angular to create interactive widgets from vanilla (non-Angular) HTML?

I'm building an Angular application: a training engine which will present online courses to the user.
Each course is basically a series of "slides" - HTML partials which the user can navigate through in sequence. Each slide can include zero or more interactive widgets of varying types: simple quizzes, hands-on exercises, etc.
My goal is for the courses to consist of pure HTML/CSS, so that less technical folks can build courses without having to get their hands dirty with JS or Angular. That's fine as long as courses only contain static HTML. But it gets tricky when I want to add the interactive widgets to a course.
For example, a sample course "slide" might look like this:
<p>Here's some static content introducing the quiz.</p>
<div class="quiz">
<ol class="questions" data-passing-score="50">
<li>
<p>What was Abraham Lincoln's last name?</p>
<ul class="answers">
<li>Smith</li>
<li>Johnson</li>
<li class="correct">Lincoln</li>
<li>Liebowitz</li>
</ul>
</li>
<li>
<p>What were George Washington's false teeth made of?</p>
<ul class="answers">
<li>Particle board</li>
<li class="correct">Wood</li>
<li>The bones of his enemies</li>
<li>Advanced space-age polymers</li>
</ul>
</li>
</ol>
</div>
<p>Here's some static content that appears after the quiz.</p>
...and, when this HTML file gets loaded (presumably via $http.get()), my application would notice that it contains a div with the class quiz, and would set up the needed interactivity: tweaking the structure of the markup (e.g., adding radio buttons and a submit button), perhaps hiding and showing elements (so the user would only see one question at a time), scoring the quiz on submission, etc.
This is a pretty common pattern in jQuery-land. Of course, we're not in jQuery-land.
If I'm thinking about this correctly, there are two problems I would need to solve to make this work.
Problem 1: First, I would need to get the quiz data out of the raw HTML, and into a JavaScript object. For example, I might parse the HTML above into a structure like this:
var quiz = {
passing_score: 50,
questions: [
{
ask: "What was Abraham Lincoln's last name?",
answers: [
{ text: "Smith", correct: false },
{ text: "Johnson", correct: false },
{ text: "Lincoln", correct: true },
{ text: "Liebowitz", correct: false }
]
},
...
]
};
I guess I'd want to convert the loaded HTML into a DOM tree (just in memory, not appended to the document), and then explore it (using jQuery or jqLite) to find the data I'm interested in.
Is that a sensible approach? Are there other approaches I might want to explore?
Problem 2: Second, I would need to replace div.quiz in the loaded HTML with the contents of a quiz template, like this:
<form ng-controller="QuizController as quizCtrl" ng-submit="quizCtrl.submit()">
<ol>
<li ng-repeat="question in quizCtrl.questions">
<p ng-bind-html="question.ask"></p>
<ul>
<li ng-repeat="answer in question.answers">
<label>
<input type="radio" ng-attr-name="{{ 'q' + $parent.$index }}" ng-model="question.selected_answer" ng-value="answer">
<span ng-bind-html="answer.text"></span>
</label>
</li>
</ul>
</li>
</ol>
<button type="submit">Score Quiz</button>
</form>
...and bind that div to QuizController.
How do I dynamically bind a particular DOM node to a particular controller? How do I get the quiz object (which I constructed in the previous step) into the controller's scope?
Is there a standard-ish solution to this problem in Angular-land? Or is this entire approach just totally bananas?
Hope this makes sense. Thanks for any guidance you can provide!
The answer, as #dandavis suggested in the comments above, is custom directives.
For my quiz example, I could create a custom directive that defines a custom element. (Alternately, I could use restrict: 'C' in my directive definition to match <div class="quiz">, or restrict: 'A' to match on <div quiz>.)
Then, I just would put all my setup-and-DOM-massaging logic in the directive's link function.
Finally, I would just need to specify to the course authors how they should mark up a quiz.
I haven't fully played this out in real code yet, but I'm pretty sure this is all accurate.

Get javascript accordion to work with 1 ID

I'm using a nice accordion script from TYMPANUS for my website though the difference is that I am using it multiple times on 1 page like:
<div id="st-accordion" class="st-accordion">
<ul>
<li>
Flowers <span class="st-arrow">View <b>Details</b></span>
<div class="st-content">
<p>She packed her seven versalia, put her initial into the belt and made
herself on the way. When she reached the first hills of the Italic Mountains, she had
a last view back on the skyline of her hometown Bookmarksgrove, the headline of
Alphabet Village and the subline of her own road, the Line Lane.</p>
</div>
</li>
</ul>
</div>
<div id="st-accordion" class="st-accordion">
<ul>
<li>
Flowers <span class="st-arrow">View <b>Details</b></span>
<div class="st-content">
<p>She packed her seven versalia, put her initial into the belt and made
herself on the way. When she reached the first hills of the Italic Mountains, she had
a last view back on the skyline of her hometown Bookmarksgrove, the headline of
Alphabet Village and the subline of her own road, the Line Lane.</p>
</div>
</li>
</ul>
</div>
Please see this FIDDLE 01
As you can see I have 2 separate elements (I need it to be separate) using that accordion but because the ID is the same, you can clearly see that the second accordion isn't working.
Javascript for this fiddle:
$(function() {
$('#st-accordion').accordion();
});
So to get it working, I created separate ID's for each element like in this FIDDLE 02
Javascript for this fiddle:
$(function() {
$('#st-accordion-01, #st-accordion-02').accordion();
});
BUT I don't like having to always create an extra / different ID, so it there a way to get FIDDLE 01 working without having to resort to FIDDLE 02? ... Or is this just not possible?
*The Javascripts posted above are at the very bottom of the javascript section in jsfiddle
There can not / must not be elements with the same id name in one page.
http://www.w3.org/TR/html5/elements.html#the-id-attribute
The id attribute specifies its element's unique identifier (ID). The
value must be unique amongst all the IDs in the element's home subtree
and must contain at least one character. The value must not contain
any space characters.
So to fix your problem, just remove the same ids and just leave the class st-accordion
http://jsfiddle.net/5h559dyj/3/
$(function() {
$('.st-accordion').accordion();
});

JavaScript Regular Expression - grouping, one-or-more characters, excluding set character strings

I'm trying to match and replace broken HTML using a regex, but I've done a couple of full circles with grouping and lookbacks and quantifiers. I'm struggling to match every scenario.
JavaScript, because the issue is triggered in a Web client browser HTML editor.
The broken HTML is specific - any text between a closing LI and the closing list UL or OL, that is not properly formed as a list item.
For instance, this piece here, from the greater example underneath:
</li>
bbb<strong>bbbb</strong><strong>bbb <span style="text-decoration: underline;"><em>bbbbb</em></span></strong>=0==
</ul>
Here is the full example of where the issue could exist:
<ul>
<li>1111</li>
<li>Could be anything here</li>
<li>aaaa</li>
bbb<strong>bbbb</strong><strong>bbb <span style="text-decoration: underline;"><em>bbbbb</em></span></strong>=0==
</ul>
<ol>
<li>more?<li>
<li>echo</li>
</ol>
This is what I intend the HTML to look like using a match + replace.
<ul>
<li>1111</li>
<li>Could be anything here</li>
<li>aaaabbb<strong>bbbb</strong><strong>bbb <span style="text-decoration: underline;"><em>bbbbb</em></span></strong>=0==
</ul>
<ol>
<li>more?<li>
<li>echo</li>
</ol>
A few expressions I've tried are the following, but depending on these (or slight variations), I'm matching too much or not correctly or something:
/<\/li>.*?<\/[ou]l>/mig
/<\/li>([\s\n]*[\w!\.?;,<:>&\\\-\{\}\[\]\(\)~#'"=/]+[\s\n]*)+<\/[ou]l>/mig
/<\/li>([\s\n]*[^\s\n]+[\s\n]*)+<\/[ou]l>/i
Searched for a couple of days on and off, no luck.. I realise I'm probably asking something answered hundreds of times before.
it's recommended to use a dom based approach to pocessing html
using jQuery:
$('ul>:not(li)').wrapAll('<li></li>');

unterminated string literal

The following code:
var str= "<strong>English Comprehension<\/strong>
<br\/>
<ul>
<li> Synonyms/Antonyms/Word Meaning (Vocabulary)<\/li>
<li> Complete the Sentence (Grammar)<\/li>
<li> Spot error/Correct sentence (Grammar/sentence construction)<\/li>
<li> Sentence Ordering (Comprehension skills)<\/li>
<li> Questions based on passage (Comprehension skills)<\/li>
<\/ul>
<br\/>";
Gives the error: "unterminated string literal". Whats the problem?
You can't split a string across lines like that in javascript. You could accomplish the same readability by making each line a separate string and concatenating them with the plus sign like so:
var str = "<strong>English Comprehension</strong>"
+ "<br />"
+ "<ul>"
+ "<li>Synonyms/Antonyms/Word Meaning (Vocabulary)</li>"
and so on...
If using multiline string in JavaScript you must use '\' in the end of each line:
var str = "abc\
def\
ghi\
jkl";
Also beware extra whitespace if your code is indented.
I suggest to do it differently... have hidden element with that HTML e.g.
<div id="myHiddenDiv" style="display: none;"><strong>English Comprehension</strong>
<br />
...
</div>
Then simply read its inner HTML:
var str = document.getElementById("myHiddenDiv").innerHTML;
The big advantage is that you won't have to fight your way with literal strings and it's much easier to edit, the downside is that you add another element to the DOM. Your choice. :)
It is not a best practice to write multiline strings in Javascript.
But, you can do that with the \ terminator:
var str= "<strong>English Comprehension<\/strong>\
<br\/>\
<ul>\
<li> Synonyms/Antonyms/Word Meaning (Vocabulary)<\/li>\
<li> Complete the Sentence (Grammar)<\/li>\
<li> Spot error/Correct sentence (Grammar/sentence construction)<\/li>\
<li> Sentence Ordering (Comprehension skills)<\/li>\
<li> Questions based on passage (Comprehension skills)<\/li>\
<\/ul>\
<br\/>";
Note that trailing spaces are part of the string, so it is better to get rid of them, unless they are intentional:
var str= "<strong>English Comprehension<\/strong>\
<br\/>\
<ul>\
<li> Synonyms/Antonyms/Word Meaning (Vocabulary)<\/li>\
<li> Complete the Sentence (Grammar)<\/li>\
<li> Spot error/Correct sentence (Grammar/sentence construction)<\/li>\
<li> Sentence Ordering (Comprehension skills)<\/li>\
<li> Questions based on passage (Comprehension skills)<\/li>\
<\/ul>\
<br\/>";
A good way to concatenate strings in javascript is fallowing:
var stringBuilder = [];
stringBuilder.push("<strong>English Comprehension</strong>");
stringBuilder.push("<br />");
stringBuilder.push("<ul>");
...
var resultString = stringBuilder.join("");
This method faster than
var str = "a"+"b" +"c"
JavaScript doesn't allow literal line breaks in strings unless you escape them with \:
var str= "<strong>English Comprehension<\/strong>\
<br\/>\
<ul>\
<li> Synonyms/Antonyms/Word Meaning (Vocabulary)<\/li>\
<li> Complete the Sentence (Grammar)<\/li>\
<li> Spot error/Correct sentence (Grammar/sentence construction)<\/li>\
<li> Sentence Ordering (Comprehension skills)<\/li>\
<li> Questions based on passage (Comprehension skills)<\/li>\
<\/ul>\
<br\/>";
You might look at ES2015+ template literals instead, which use backticks instead of ' or " and allow literal line breaks:
var str= `<strong>English Comprehension<\/strong>
<br\/>
<ul>
<li> Synonyms/Antonyms/Word Meaning (Vocabulary)<\/li>
<li> Complete the Sentence (Grammar)<\/li>
<li> Spot error/Correct sentence (Grammar/sentence construction)<\/li>
<li> Sentence Ordering (Comprehension skills)<\/li>
<li> Questions based on passage (Comprehension skills)<\/li>
<\/ul>
<br\/>`;
But of course, that only works on ES2015-compatible JavaScript engines (or if you transpile).
Note that within a template literal ${...} has special meaning (allowing you to substitute the result of any expression:
let v = "Ma";
console.log(`Look ${v}, no hands!`); // Look Ma, no hands!
Remove the newlines, it will work. GOTCHA to keep in mind ;)
See it simple works all fine here without newlines:
http://jsfiddle.net/87dYh/
var str= "<strong>English Comprehension<\/strong><br\/><ul> <li> Synonyms/Antonyms/Word Meaning (Vocabulary)<\/li> <li> Complete the Sentence (Grammar)<\/li> <li> Spot error/Correct sentence (Grammar/sentence construction)<\/li> <li> Sentence Ordering (Comprehension skills)<\/li> <li> Questions based on passage (Comprehension skills)<\/li> <\/ul> <br\/>";
alert(str);
If you want to maintain your data in one block of string, try:
var str= "<strong>English Comprehension</strong>\n\
<br />\n\
<ul>\n\
<li> Synonyms/Antonyms/Word Meaning (Vocabulary)</li>\n\
<li> Complete the Sentence (Grammar)</li>\n\
<li> Spot error/Correct sentence (Grammar/sentence construction)</li>\n\
<li> Sentence Ordering (Comprehension skills)</li>\n\
<li> Questions based on passage (Comprehension skills)</li>\n\
</ul>\n\
<br />";
notice the \n\ at the end of each line.
My code was a response.write() where I needed to insert html to be outputted POST SUbmit on a form within an Iframe. When copying my code over from an text editor the carraige return carried over into the code window. Once I deleted my returns and whitespace between tags so that the code looked like a block paragraph that fixed the issue:
("loremIPSUM.<br><br><h2>Title</h2><br>loremIPSUM:<br><br><br><a href='http://url1'>lnk1</a><br><br><a href='http://url2'>loremIPSUMlnk2</a><br><br><a href='http://url3'>loremIPSUM</a><br><br><a href='url4'>IF THE case id is: "</a>"+value)

Categories

Resources