Get javascript accordion to work with 1 ID - javascript

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();
});

Related

Advanced lookarounds in regex

Hi I have the this html:
<div class="c-disruption-item c-disruption-item--line">
<h3 class="c-disruption-item__title" id="11e62827-9f9c-48b2-8807-09f6b6ebeec6" name="11e62827-9f9c-48b2-8807-09f6b6ebeec6"> <a>Closure of London Road</a> </h3>
<ul class="c-disruption__affected-entities">
<li>Affected routes:</li>
<li> <a href="/services/RB/X4#disruptions" class="line-block" style="background-color: #A38142; color:#FFFFFF">
<div class="line-block__contents">
X4
</div> </a> </li>
</ul>
<p>The left turn from Wiltshire Road on to London Road will be closed between 10.00pm and 5.00am on the nights of 27/28 and 28/29 April 2020.<br> <br> Lion X4 affected as follows:-<br> <br> Journeys towards Bracknell will be diverted and unable to serve the Seaford Road bus stop. Please use the Three Frogs bus stop instead.<br> <br> Journeys towards Reading are not affected and should follow normal route.<br> <br> We are sorry for the inconvenience caused.</p>
</div>
And I want to select whatever comes before and after the <ul></ul> section meaning not this:
<ul class="c-disruption__affected-entities">
<li>Affected routes:</li>
<li> <a href="/services/RB/X4#disruptions" class="line-block" style="background-color: #A38142; color:#FFFFFF">
<div class="line-block__contents">
X4
</div> </a> </li>
</ul>
But! if this section does not exist i want to select all.
I tried this selection ([\W\w]+(?=\<ul)|(?<=ul>)[\W\w]+) but it doesn't work if the <ul><\ul> not exist.
The selection have to be regax alone.
Does somebody have an idea?
thanks
Regex is the last resort (at least when using JavaScript). Your objective is done by traversing the DOM not scanning a huge string trying to match error prone patterns.
Finding an unordered list with the className of ".c-disruption__affected-entities" and then excluding said <ul>.
Regex
String is the only data type regex is equipped to deal with. So all of the HTML (which is much more than just string) needs to be converted into a string.
let htmlString = document.body.innerHTML;
Valid HTML may use double and single quotes, multiple white-spaces may occur, multiple empty-lines, etc. A regex must be written to be able to handle such inconsistencies or written to target a pattern so specific that its usefulness outside of that particular situation makes it worthless. The htmlString will most likely be a hot mess of thickly layered HTML sporting huge attribute values like: "c-disruption-item c-disruption-item--line" Anyways, here's a statement using the regex method .replace(). It's untested because it's not efficient, nor practical to use, a complete waste of time:
let result = htmlString.replace(/<ul\s[\s\S]*c-disruption__affected-entities[\s\S]*ul>/i, '');
DOM
A value like this: ul.c-disruption__affected-entities has more meaning as HTML, and is accessible as a DOM Object several standard ways. The following demo features a function that easily meets OP's objective.
Demo
Note: Details are commented in demo.
/**
* Create a documentFragment and move the excluded node
* (or nodes if it has descendants) to it. Although the
* excluded node is no longer part of the DOM, a
* documentFragment allows any of its descendant nodes to
* reattach to the DOM however and whenever.
***
* #param {String} selector -- A CSS selector string of a
* tag that needs to be
* returned without the
* excluded tag.
* {String} exclusion - A CSS selector string of the
* tag that needs to be
* removed from the returned
* value.
*/
const excludeNode = (selector, exclusion) => {
const frag = document.createDocumentFragment();
const area = document.querySelector(selector);
const excl = area.querySelector(exclusion);
frag.appendChild(excl);
return area.outerHTML;
};
console.log(excludeNode(".c-disruption-item.c-disruption-item--line", ".c-disruption__affected-entities"));
:root {
overflow-y: scroll;
height: 200vh
}
<div class="c-disruption-item c-disruption-item--line">
<h3 class="c-disruption-item__title" id="11e62827-9f9c-48b2-8807-09f6b6ebeec6" name="11e62827-9f9c-48b2-8807-09f6b6ebeec6"> <a>Closure of London Road</a> </h3>
<ul class="c-disruption__affected-entities">
<li>Affected routes:</li>
<li>
<a href="/services/RB/X4#disruptions" class="line-block" style="background-color: #A38142; color:#FFFFFF">
<div class="line-block__contents">
X4
</div>
</a>
</li>
</ul>
<p>The left turn from Wiltshire Road on to London Road will be closed between 10.00pm and 5.00am on the nights of 27/28 and 28/29 April 2020.<br> <br> Lion X4 affected as follows:-<br> <br> Journeys towards Bracknell will be diverted and unable to serve
the Seaford Road bus stop. Please use the Three Frogs bus stop instead.<br> <br> Journeys towards Reading are not affected and should follow normal route.<br> <br> We are sorry for the inconvenience caused.</p>
</div>

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.

How can I structure my MustacheJS template to add dynamic classes based on the values from a JSON file?

OBJECTIVE
To build an app that allows the user to search for locations.
CURRENT STATE
At the moment the locations listed are few, so they are just all presented when landing on the "dealers" page.
BACKGROUND
Previously there were only about 50 showrooms carrying a product we sell, so a static HTML page was fine.
And displays as
But the page size grew to about 1500 lines of code after doing this. We have gotten more and need a scalable solution so that we can add many more dealers fast. In other projects, I have previously used MustacheJS and to load in values from a JSON file. I know the ideally this will be an AJAX application. Perhaps I might be better off with database here?
Below is what I have in mind so far, and it "works" up to a certain point, but seems not to be anywhere near the most sustainable solution that can be efficiently scaled.
HTML
<a id="{{state}}"></a>
<div>
<h4>{{dealer}} : {{city}}, {{state}} {{l_type}}</h4>
<div class="{{icon_class}}">
<ul>
<li><i class="icon-map-marker"></i></li>
<li><i class="icon-phone"></i></li>
<li><i class="icon-print"></i></li>
</ul>
</div>
<div class="listingInfo">
<p>{{street}} <br>{{suite}}<br>
{{city}}, {{state}} {{zip}}<br>
Phone: {{phone}}<br>
{{toll_free}}<br>
{{fax}}
</p>
</div>
</div>
<hr>
JSON
{ "dealers" : [
{
"dealer":"Benco Dental",
"City":"Denver",
"state":"CO",
"zip":"80112",
"l_type":"Showroom",
"icon_class":"listingIcons_3la",
"phone":"(303) 790-1421",
"toll_free":null,
"fax":"(303) 790-1421"
},
{
"dealer":"Burkhardt Dental Supply",
"City":"Portland",
"state":"OR",
"zip":"97220",
"l_type":"Showroom",
"icon_class":"listingIcons",
"phone":" (503) 252-9777",
"toll_free":"(800) 367-3030",
"fax":"(866) 408-3488"
}
]}
CHALLENGES
The CSS class wrapping the ul will vary based on how many fields there are. In this case there are 3, so the class is "listingIcons_3la"
The "toll free" number section should only show up if in fact, there is a toll free number.
the fax number should only show up if there is a value for a fax number.
For conditionals, you can use
{{#toll_free}}
{{toll_free}}
{{/toll_free}}
Which will only display the content in between the tags if it is not null, undefined, or false. Otherwise, it will completely ignore that content.
For the listing icons class, you should simplify your html to use the li for both the icon and the text. That way you can surround the whole thing in conditionals from Mustache and avoid having to deal with using such classes entirely. An example would be:
{{#toll_free}}
<li><i class="icon-phone"></i>{{toll_free}}</li>
{{/toll_free}}
Using CSS, you can achieve the same look you have now with some padding. I made a fiddle to show this and the working template code, based roughly on your example: http://jsfiddle.net/NGwge/
You may want to check out mustache conditionals (with a poc) at http://www.elated.com/res/File/articles/development/easy-html-templates-with-mustache/sections-conditional.html. That will take care of 2 and 3
With the ".length" piece from Mustache Conditions and Loops, you should be able to accomplish 1
The CSS class wrapping the ul will vary based on how many fields
there are. In this case there are 3, so the class is
"listingIcons_3la"
The "toll free" number section should only show up if in fact, there
is a toll free number.
the fax number should only show up if there is a value for a fax
number.

When click on title, make information appear on another div for the information?

I'm looking for a jQuery solution for this. When I click, let's say on a artist name. Theirs another div beside that has the information showing up. (possibly in a animation/fading way)
Can anyone help?
Link and Info Within Physical Proximity
Use this method if your artist link and artist info is within physical proximity (i.e., sibling in the DOM). Let's say you have:
<a ... class="artistInfo">Artist name</a>
<div class="artistInfo"> .... shown on link click </div>
... repeat
Then use:
$('div.artistInfo').hide(); // Initially hide info
$('a.artistLink').click(function() { // Show on click
$(this).next('div.artistInfo').fadeIn();
});
next() is used to avoid wrapping each link-info set in "per-artist" container. Thus, you should be able to have multiple "sets" of link-plus-info in the same page.
Link and Info in Different Places
When there is no physical proximity between the link and tha info, you will need some sort of identifiers from the link to the info*. E.g.,
<a ... class="artistLink" id="artistLink-1">Artist name</a>
...
<div class="artistInfo" id="artistInfo-1"> .... shown on link click </div>
You can now:
$('div.artistInfo').hide(); // Initially hide all infos
$('a.artistLink').click(function() { // Show on click (by reference)
var n = $(this).attr('id').substring(11); // Remove "artistLink-"
$('#artistInfo' + n).fadeIn();
});
*) Note: You could use the DOM index if links and infos are ordered similarly. I.e., the nth <a> element corresponds to the n info element.
Use the .html method of jquery
If by 'beside' you mean it is the very next sibling, you can use next:
$(".artist").click(function() {
$(this).next("div").slideToggle("slow");
});
<span class="artist">Foo and the Jackals</span>
<div>some content</div>
<span class="artist">Jeff and the misshapen lunatics</span>
<div>some content</div>
...
slideToggle will "Display or hide the matched elements with a sliding motion.".
There can be a number of ways to do this. But then it actually depends on how exactly you want it. one possible way can be use of fadeIn method on a hidden DIV.
<div class='box' style="display:none;">
<p> Artist Description </p>
</div>
<p id='abcd'>Artist Name</p>
for example on this code use this jquery
jQuery('#abcd').click(function(){
jQuery('.box').fadeIn();
});

Jquery .next IE6/7 issue

I've been having nothing but problems with this script for a simple hide/show gallery of testimonials.
I think the java is somewhat self explanatory...
When the page loads, I tell it to show the first testimonial in the line up (as the css is display:none) and gives it a selected class name. Works fine in across the board.
On click I want it to remove the selected class place it to another, and then hide the first testimonial and pull up a corresponding one. Except all that happens here is the first testimonial disappears (hide) and the selected class is added.
<script type="text/javascript">
$(document).ready(function(){
$(".testimonial:first").show();
$("li.testID:first").addClass("selectedName");
$("li.testID").click(function(){
$("li.testID").removeClass("selectedName");
$(this).addClass("selectedName");
$(".testimonial").hide();
$(this).next(".testimonial").fadeIn("slow");
});
});
</script>
Example of markup
<ul id="testName">
<li class="testID">Persons Name</li>
<blockquote class="testimonial">
<span class="bqStart">“</span>
Testimoinal here
<span class="bqEnd">”</span><br /><br />
<span class="testAuthor"><b>Name</b><a target="_blank" href="#">Website</a> Company</span>
</blockquote>
As a side note this is working fine in FF and Safari
Your help is greatly appreciated.
Thanks.
It's probably not working in IE because it's not valid markup: you can't have a blockquote as a direct child of a UL, so IE probably stores them in some weird place in the DOM tree which means .next doesn't find them. Can you move the blockquote elements into the li?
Is .testimonial the class you have given to your lists? <ul> or <ol>
It seems as if you are trying to get the next testimonial list to show, when infact you are actually getting the next <li> which has a class of .testimonial which I suspect is incorrect as you are using .testID as the class for your list items.
Please try this instead:
$(this).parent().next(".testimonial").fadeIn("slow");

Categories

Resources