Accessible jQuery toggles odd behaviour - javascript

I'm setting up a reusable jQuery toggle function (for my employer) to enable show/hide of content (eg FAQs), to improve accessibility over existing inline onClick bindings.
In JSFiddle everything is fast and fluid, but when I transfer to the preview server an odd behaviour becomes apparent. Clicking the button works as normal, but on clicking the button's link text there's a noticeable delay before anything happens.
Is there anything within the script that might be causing the delay? Or anything which is an obvious candidate for conflicts of some kind?
Anyway, here's the code (based on Mike Raynham's accessible toggles):
http://jsfiddle.net/internet_man/9yKKM/2/
html:
<div class="buttons">
<ul>
<li><strong class="_toggle type noscript">This is the first toggle</strong>
<ul class="_toggle-this show-hide">
<li><strong>Option One</strong></li>
<li><strong>Option Two</strong></li>
<li><strong>Option Three</strong></li>
</ul>
</li>
<li class="bad-break"><strong class="_toggle type noscript">This is the second toggle (a bit longer than the first)</strong>
<ul class="_toggle-this show-hide">
<li><strong>Option One</strong></li>
<li><strong>Option Two</strong></li>
</ul>
</li>
<li class="bad-break"><strong class="_toggle type noscript">This is the third toggle (also somewhat longer)</strong>
<ul class="_toggle-this show-hide">
<li><strong>Option One</strong></li>
<li><strong>Option Two</strong></li>
</ul>
</li>
</ul>
</div>
Script:
var create_name = function(text) {
var name = text.toLowerCase();
name = name.replace(/^\s+|\s+jQuery|[^a-z0-9&\s-]/g, '');
name = name.replace(/&/g, 'and');
name = name.replace(/\s/g, '-');
name = name.replace(/(-)+\1/g, "jQuery1");
return name;
};
var add_link = function() {
var name = create_name(jQuery(this).text());
jQuery(this).next('.toggle-this').attr('name', name);
jQuery(this).html('' + jQuery(this).html() + '');
};
var toggle = function(event) {
event.preventDefault();
jQuery(this).toggleClass('expanded').nextAll('.toggle-this').slideToggle(100);
jQuery('.toggle-this').not(jQuery(this).siblings()).slideUp(100);
jQuery('.toggle').not(jQuery(this)).removeClass('expanded');
};
var remove_focus = function() {
jQuery(this).blur();
};
jQuery(function (){
jQuery('._toggle').removeClass('_toggle, noscript').addClass('toggle');
jQuery('._toggle-this').removeClass('_toggle-this').addClass('toggle-this');
jQuery('._expanded').removeClass('_expanded').addClass('expanded');
jQuery('.toggle:not(.expanded)').nextAll('.toggle-this').hide();
jQuery('.toggle').each(add_link);
jQuery('.toggle').click(toggle);
jQuery('.toggle a').mouseup(remove_focus);
});
CSS:
body
{
font-size:62.5%;
color:#666;
font-family:arial,Helvetica,sans-serif;
}
a
{
color:#462170;
}
ul
{
list-style-type:none;
margin-left:0;
padding-left:0;
}
strong
{
font-weight:normal;
}
div.buttons
{
width:462px;
line-height:2.2em;
margin:1.5em 0;
}
.buttons li > strong
{
font-size:1.9em;
}
.toggle, .buttons .type.noscript
{
display:block;
font-size:1.9em;
height:65px;
background:url(http://oi48.tinypic.com/dy6xf.jpg) 0 -85px no-repeat;
padding:20px 0 0 90px;
text-decoration:none;
color:#462170;
cursor:pointer;
cursor:hand;
}
.toggle a
{
text-decoration:none;
}
.toggle strong
{
display:block;
height:65px;
border:1px dotted red;
}
.toggle:hover, .toggle:focus
{
background-position:0 0;
}
.buttons .show-hide
{
border-bottom:6px solid white;
}
.buttons .show-hide li
{
margin-left:12px;
border-bottom: 2px solid white;
border-top:2px solid white;
font-size:1em;
}
.buttons .show-hide a
{
display:block;
height:15px;
text-decoration:none;
color:#462170;
background:#f1f1f1 url(http://oi46.tinypic.com/6iedtw.jpg) 5px 14px no-repeat;
padding:12px 0 15px 58px;
}
.buttons .show-hide a:hover, .connection-buttons .show-hide a:focus
{
text-decoration:underline;
color:black;
background-color:#ece8f0;
background-position:5px -41px;
}
li.bad-break a
{
padding-right:30%;
}
Notes:
Scripts on -- ._toggle becomes .toggle, ._toggle-this becomes .toggle-this, .noscript is removed and a link is inserted around (in this case) the strong element of class toggle, pointing to the next '.toggle-this' element.
Scripts off -- No toggle created but it's styled as though the accordions are expanded.

Related

Is my code to change the CSS of an element when clicked efficient?

I want to know if there is a more efficient way to change the CSS of multiple elements when one is clicked.
I have working code whereby clicking on a subheading within the "About" section changes its color, the color of the other subheadings, and the opacity of reach respective paragraph. It looks like a lot of code to me. I imagine there is a simpler or at least more streamlined way to accomplish these things that I just don't know with my borderline intermediate JS skills.
const about = () => {
const paraOne = document.getElementById("para-one");
const paraTwo = document.getElementById("para-two");
const paraThree = document.getElementById("para-three");
const mission = document.querySelector(".mission");
const value = document.querySelector(".value");
const vision = document.querySelector(".vision");
mission.addEventListener('click', () => {
mission.classList.add("active");
value.classList.remove("active");
vision.classList.remove("active");
paraOne.classList.remove('zero');
paraTwo.classList.add('zero');
paraThree.classList.add('zero');
});
value.addEventListener('click', () => {
value.classList.add("active");
mission.classList.remove("active");
vision.classList.remove("active");
paraOne.classList.add('zero');
paraTwo.classList.remove('zero');
paraThree.classList.add('zero');
});
vision.addEventListener('click', () => {
vision.classList.add("active");
mission.classList.remove("active");
value.classList.remove("active");
paraOne.classList.add('zero');
paraTwo.classList.add('zero');
paraThree.classList.remove('zero');
});
}
about();
#about-center ul li:hover,
#about-center ul li.active {
color: black;
cursor: pointer;
}
.active {
color: black;
cursor: pointer;
}
#container {
position: relative;
}
#container p {
position: absolute;
left: 30%;
}
.zero {
opacity: 0;
}
#about ul,
#about p,
#about button {
margin-bottom: 2em;
}
#about-center p {
font-size: 2rem;
}
<div id="about-center">
<h1>About Us</h1>
<ul>
<li class="mission active">OUR MISSION</li>
<li class="value">our values</li>
<li class="vision">our vision</li>
</ul>
<div id="container">
<p id="para-one">This is our mission.
</p>
<p id="para-two" class="zero">These are our values.
</p>
<p id="para-three" class="zero">This is our vision.
</p>
</div>
<button>read more</button>
</div>
You can loop over all the li elements. Use this to add the event listeners initially. Then in the listener, you can loop again to do the appropriate class changes for the element that the user clicked on versus the other elements. And you can use the index of the iteration to change the class of the corresponding p element.
const about = () => {
const lis = document.querySelectorAll("#about-list li");
const paras = document.querySelectorAll("#container p");
lis.forEach(li => li.addEventListener("click", (e) => lis.forEach((li1, index) => {
if (li1 == e.target) {
li1.classList.add("active");
paras[index].classList.remove("zero");
} else {
li1.classList.remove("active");
paras[index].classList.add("zero");
}
})));
}
about();
#about-center ul li:hover,
#about-center ul li.active {
color: black;
cursor: pointer;
}
.active {
color: black;
cursor: pointer;
}
#about-center ul li {
color: grey;
}
#container {
position: relative;
}
#container p {
position: absolute;
left: 30%;
}
.zero {
opacity: 0;
}
#about ul,
#about p,
#about button {
margin-bottom: 2em;
}
#about-center p {
font-size: 2rem;
}
<div id="about-center">
<h1>About Us</h1>
<ul id="about-list">
<li class="mission active">OUR MISSION</li>
<li class="value">our values</li>
<li class="vision">our vision</li>
</ul>
<div id="container">
<p id="para-one">This is our mission.
</p>
<p id="para-two" class="zero">These are our values.
</p>
<p id="para-three" class="zero">This is our vision.
</p>
</div>
<button>read more</button>
</div>
I think this is as effective as it could be. Now, there are things which can be done to make it more readable (especially if you get more than three tabs later), but that won't change the effectiveness or would actually make it worse.
Like ... it would be more readable to make function which will turn "off" all tabs and call it from all click handlers, although technically it means it will be needlessly removing class which is not set on one of them.
Also, if you put the matching paragraphs IDs into HTML, you can use just one function for all and use event.currentTarget to recognize which one it was called for. Again, not worth the time if you have just three tabs, but if you have more ...

Creating a tag box

I'm making a tag box similar to that of StackOverflow. I see that this question has already been asked here (How to make a "tags box" using jQuery (with text input field + tags separated by comma)) but I'm having a question with regards to the Javascript.
HTML:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='tag-container' id = "tag-container">
<span class='dashfolio-tag'>Tag1</span>
<span class='dashfolio-tag'>Tag2</span>
<span class='dashfolio-tag'>Tag3</span>
</div>
<input type="text" value="" placeholder="Add a tag" id = "add-tag-input" />
CSS:
.tag-container {
max-width: 300px; /* helps wrap the tags in a specific width */
}
.dashfolio-tag {
cursor:pointer;
background-color: blue;
padding: 5px 10px 5px 10px;
display: inline-block;
margin-top: 3px; /*incase tags go in next line, will space */
color:#fff;
background:#789;
padding-right: 20px; /* adds space inside the tags for the 'x' button */
}
.dashfolio-tag:hover{
opacity:0.7;
}
.dashfolio-tag:after {
position:absolute;
content:"×";
padding:2px 2px;
margin-left:2px;
font-size:11px;
}
#add-tag-input {
background:#eee;
border:0;
margin:6px 6px 6px 0px ; /* t r b l */
padding:5px;
width:auto;
}
JS:
$(document).ready(function(){
$(function(){
$("#add-tag-input").on({
focusout : function() {
var txt = this.value.replace(/[^a-z0-9\+\-\.\#]/ig,''); // allowed characters
if(txt) $("<span/>", {text:txt.toLowerCase(), insertBefore:this});
this.value = "";
},
keyup : function(ev) {
// if: comma|enter (delimit more keyCodes with | pipe)
if(/(188|13)/.test(ev.which)) $(this).focusout();
}
});
$('.tag-container').on('click', 'span', function() {
if(confirm("Remove "+ $(this).text() +"?")) $(this).remove();
});
});
});
My question is pretty simple, how do I add the new input as a span with class dashfolio-tag inside the #tag-container? I dabbled with the insertBefore property trying to add it to the right node, but to no avail. Thanks in advance guys!
Change this line of code,
if(txt) $("<span/>", {text:txt.toLowerCase(), insertBefore:this});
to
if(txt) {
$("<span/>", {
text:txt.toLowerCase(),
appendTo:"#tag-container",
class:"dashfolio-tag"
});
}
See the demo: https://jsfiddle.net/2gvdsvos/4/
To fix the margins in between tags,
Update HTML to,
<div>
<div class="tag-container" id="tag-container">
<span class='dashfolio-tag'>tag1</span>
<span class='dashfolio-tag'>tag2</span>
<span class='dashfolio-tag'>tag3</span>
</div>
</div>
<div>
<input type="text" value="" placeholder="Add a tag" id="add-tag-input" />
</div>
And add this to CSS,
.tag-container:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.dashfolio-tag {
...
margin-right: 4px;
float: left;
}
Hope this helps! ;)
https://jsfiddle.net/2gvdsvos/5/

Overriding element.style

I am trying to make a responsive web page eg where the display changes with the screen size.
I have the following HTML:
<div class="responsive_menu" id="resp_Menu" onClick="fnResponsiveMenu()">
</div>
<div class="nav_wrapper">
<div class="navigation" id="navMenu1">
Contact Us
</div>
<div class="navigation" id="navMenu2">
Information
</div>
<div class="navigation" id="navMenu3">
Venue Details
</div>
<div class="navigation" id="navMenu4">
Registration & Payment
</div>
<div class="navigation" id="navMenu5">
Agenda
</div>
<div class="navigation" id="navMenu6">
Home
</div>
</div>
and the following in responsive.css:
.navigation {
width:100%;
background-color:#3171B7;
border-bottom:1px solid #ffffff;
margin:0;
text-transform:uppercase;
font-size:16px;
height:40%;
padding-top:10px;
display:none;
}
and the following in style.css:
.navigation {
float:right;
margin:30px 0px 1px 0px;
height:25px;
width:100px;
display:block;
}
.navigation a
{
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
color: #FFFFFF;
text-decoration: none;
display:block;
padding:0 5px;
height:30px;
}
and the following JavaScript:
var oNavChecker = 0;
function fnResponsiveMenu()
{
if(oNavChecker == 0 )
{
var oNav1 = document.getElementById("navMenu1");
oNav1.setAttribute('style', 'display:inline');
var oNav2 = document.getElementById("navMenu2");
oNav2.setAttribute('style', 'display:inline');
var oNav3 = document.getElementById("navMenu3");
oNav3.setAttribute('style', 'display:inline');
var oNav4 = document.getElementById("navMenu4");
oNav4.setAttribute('style', 'display:inline');
var oNav5 = document.getElementById("navMenu5");
oNav5.setAttribute('style', 'display:inline');
var oNav6 = document.getElementById("navMenu6");
oNav6.setAttribute('style', 'display:inline');
oNavChecker = 1;
return;
}
if(oNavChecker == 1 )
{
var oNav1 = document.getElementById("navMenu1");
oNav1.setAttribute('style', 'display:none');
var oNav2 = document.getElementById("navMenu2");
oNav2.setAttribute('style', 'display:none');
var oNav3 = document.getElementById("navMenu3");
oNav3.setAttribute('style', 'display:none');
var oNav4 = document.getElementById("navMenu4");
oNav4.setAttribute('style', 'display:none');
var oNav5 = document.getElementById("navMenu5");
oNav5.setAttribute('style', 'display:none');
var oNav6 = document.getElementById("navMenu6");
oNav6.setAttribute('style', 'display:none');
oNavChecker = 0;
return;
}
}
That all works fine and behaves as I want.
My problem is, once the screen has been reduced, and then the menu button pressed eg when fnResponsiveMenu() has been:
pressed to first show the menu
then pressed again to remove it
then the screen return to wide resolution,
The navMenus do not show anymore.
I am sure it is because the CSS is being over riden by the javascript eg:
oNav1.setAttribute('style', 'display:none');
So how can I resolve this and get the style.CSS to override the changes applied by the JavaScript?
If this is not possible, can anyone suggest an alternative to achieveing my aim of making a responsive menu?
I can't comment, not enought rep, so I'll ask it in the answer...
Have you read this? Remove inline css of an HTML elements
Once you remove inline styling, default from your css file will be applied again.

hide divs and highlighting selected thumbnail to active

I'm creating a team members page and I used this thread to help me out a little.
Fade Out/Fade In of List Items
I have everything working as I want except I can't figure out how to make the other people in
my gallery stay active (red border) and hide the other description of the team members on page load. ( On page load scroll down to see what I mean. )
Here is the code I'm working with.
HTML
<div class="grid_6">
<div id="staffDirectory">
<ul class="team list-image clearfix">
<li class="selectedMember">
<img src="images/team/head1.jpg" class="max-img headshots" />
</li>
<li><img src="images/team/head2.jpg" class="max-img headshots" /></li>
<li><img src="images/team/head3.jpg" class="max-img headshots" /></li>
...
</ul>
</div>
</div><!--End 6-->
<div class="grid_6">
<div id="staffMember">
<ul>
<li class="staffSelected">
<div class="box white-bg">
<img src="images/team/head1.jpg" class="headshots-red" />
<h2 class="red3-tx bold">John Doe 1</h2>
<h3 class="blue4-tx" style="font-weight:400; font-style:italic;">Position: Manager</h3>
<p class="blue3-tx">text</p>
</div>
</li>
...
</ul>
</div>
CSS
#staffDirectory ul li
{
opacity: 0.9;
}
#staffDirectory li:hover{
opacity: 1;
}
.selectedMember {
opacity: 1.0 !important;
}
.staffSelected {
display: inherit;
}
#staffMember li:not(.staffSelected) {
display: none;
}
.team li{
display:inline-block;
float:left;
margin-bottom: 10px;
margin-right: 8%;
width:28%;
cursor: pointer;
}
.headshots{
border:5px solid #034A68;
}
.headshots:hover{
border:5px solid #981B1E;
}
.headshots:active{
border:5px solid #981B1E;
}
.headshots-red{
border:5px solid #981B1E;
margin-bottom:25px;
height: auto;
width: 98%;
}
JS
<script>
$(document).ready(function()
{
$("#staffDirectory ul li").click(function()
{
var index = $("#staffDirectory ul li").index(this);
var newMember = null;
newMember = $("#staffMember ul li").get(index);
$(".staffSelected").fadeOut(500);
setTimeout(function() {
$(newMember).fadeIn(500).addClass('staffSelected');
}, 500);
});
});
</script>
Sorry for the code dumb, just not sure where the problem is.
Any help would be appreciated.
Thank you.
EDIT!!! WORKING VERSION
Here is the completed working version of this employee gallery. Thank you to the people that helped me out. Hope someone else can find this useful.
http://codepen.io/daugaard47/pen/ctHru
First off all: Red border.
CSS
.selectedMember>img {
border-color: #981B1E;
}
Second: Hide unselected team members
CSS
#staffMember li {
display:none;
}
#staffMember li.staffSelected {
display:inherit;
}
Note that you created the second #staffMember with the 'staffSelected' class. Only the first one has to have it..
EDIT
Try this JS script:
$(document).ready(function()
{
$("#staffDirectory ul li").click(function()
{
var index = $("#staffDirectory ul li").index(this);
$('.staffSelected').fadeOut(500);
$('.selectedMember').removeClass('selectedMember');
$(this).addClass('selectedMember');
setTimeout(function() {
$('.staffSelected').removeClass('staffSelected');
$("#staffMember ul li:eq("+index+")").fadeIn(500).addClass('staffSelected');
}, 500);
});
});
Try simulating a click event on the first element of the directory after you define the click handler:
$(document).ready(function() {
$("#staffDirectory ul li").click(function() {
var index = $("#staffDirectory ul li").index(this),
newMember = $("#staffMember ul li").get(index);
$(".staffSelected").fadeOut(500, function () {
$(this).removeClass('staffSelected');
$(newMember).fadeIn(500).addClass('staffSelected');
});
});
$("#staffDirectory ul li:first-child").click();
});

Creating a grid overlay over image

I made a script (using mootools library) that is supposed to overlay an image with a table grid and when each grid cell is clicked/dragged over its background color changes 'highlighting' the cell.
Current code creates a table and positions it over the element (el, image in this case). Table was used since I am planning to add rectangle select tool later on, and it seemed easiest way to do it.
<html>
<head>
<title></title>
<script type="text/javascript" src="mootools.js"></script>
<script type="text/javascript">
var SetGrid = function(el, sz, nr, nc){
//get number of rows/columns according to the 'grid' size
numcols = el.getSize().x/sz;
numrows = el.getSize().y/sz;
//create table element for injecting cols/rows
var gridTable = new Element('table', {
'id' : 'gridTable',
'styles' : {
'width' : el.getSize().x,
'height' : el.getSize().y,
'top' : el.getCoordinates().top,
'left' : el.getCoordinates().left
}
});
//inject rows/cols into gridTable
for (row = 1; row<=numrows; row++){
thisRow = new Element('tr', {
'id' : row,
'class' : 'gridRow'
});
for(col = 1; col<=numcols; col++){
thisCol = new Element('td', {
'id' : col,
'class' : 'gridCol0'
});
//each cell gets down/up over event... down starts dragging|up stops|over draws area if down was fired
thisCol.addEvents({
'mousedown' : function(){
dragFlag = true;
startRow = this.getParent().get('id');
startCol = this.get('id');
},
'mouseup' : function(){
dragFlag = false;
},
'mouseover' : function(){
if (dragFlag==true){
this.set('class', 'gridCol'+$$('#lvlSelect .on').get('value'));
}
},
'click' : function(){
//this.set('class', 'gridCol'+$$('#lvlSelect .on').get('id').substr(3, 1) );
str = $$('#lvlSelect .on').get('id');
alert(str.substr(2, 3));
}
});
thisCol.inject(thisRow, 'bottom');
};
thisRow.inject(gridTable, 'bottom');
};
gridTable.inject(el.getParent());
}
//sens level selector func
var SetSensitivitySelector = function(el, sz, nr, nc){
$$('#lvlSelect ul li').each(function(el){
el.addEvents({
'click' : function(){
$$('#lvlSelect ul li').set('class', '');
this.set('class', 'on');
},
'mouseover' : function(){
el.setStyle('cursor','pointer');
},
'mouseout' : function(){
el.setStyle('cursor','');
}
});
});
}
//execute
window.addEvent('load', function(){
SetGrid($('imagetomap'), 32);
SetSensitivitySelector();
});
</script>
<style>
#imagetomapdiv { float:left; display: block; }
#gridTable { border:1px solid red; border-collapse:collapse; position:absolute; z-index:5; }
#gridTable td { opacity:0.2; filter:alpha(opacity=20); }
#gridTable .gridCol0 { border:1px solid gray; background-color: none; }
#gridTable .gridCol1 { border:1px solid gray; background-color: green; }
#gridTable .gridCol2 { border:1px solid gray; background-color: blue; }
#gridTable .gridCol3 { border:1px solid gray; background-color: yellow; }
#gridTable .gridCol4 { border:1px solid gray; background-color: orange; }
#gridTable .gridCol5 { border:1px solid gray; background-color: red; }
#lvlSelect ul {float: left; display:block; position:relative; margin-left: 20px; padding: 10px; }
#lvlSelect ul li { width:40px; text-align:center; display:block; border:1px solid black; position:relative; padding: 10px; list-style:none; opacity:0.2; filter:alpha(opacity=20); }
#lvlSelect ul li.on { opacity:1; filter:alpha(opacity=100); }
#lvlSelect ul #li0 { background-color: none; }
#lvlSelect ul #li1 { background-color: green; }
#lvlSelect ul #li2 { background-color: blue; }
#lvlSelect ul #li3 { background-color: yellow; }
#lvlSelect ul #li4 { background-color: orange; }
#lvlSelect ul #li5 { background-color: red; }
</style>
</head>
<body>
<div id="imagetomapdiv">
<img id="imagetomap" src="1.png">
</div>
<div id="lvlSelect">
<ul>
<li value="0" id="li0">0</li>
<li value="1" id="li1">1</li>
<li value="2" id="li2">2</li>
<li value="3" id="li3">3</li>
<li value="4" id="li4">4</li>
<li value="5" id="li5" class="on">5</li>
</ul>
</div>
</body>
</html>
There are two problems: while it works just fine in FF, IE and Chrome do not create the table if the page is refreshed. If you go back to directory root and click on the link to the file the grid table is displayed, if you hit 'refresh' button -- the script runs but the table is not injected.
Secondly, although the table HTML is injected in IE, it does not display it. I tried adding nbsp's to make sure its not ignored -- to no avail.
Any suggestions on improving code or help with the issues is appreciated.
Thanks!
Try adding a docType dec at the top of the page IE:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

Categories

Resources