Duplicating a division with child elements and buttons - javascript

I'm new to stack overflow so sorry if I don't articulate my problems well or show my code properly. working on a homework assignment where I have to create a game like the classic mastermind one. I have designed the game board and have created a 'round 1' division with toggling color buttons, submit button, and even calls up the proper result image (combination of white & black dots as img png).
My js code for duplicating the rounds is below. my problem is duplicating the round and all the functionality 9 more times. I would like to disable the toggle color buttons of a round once it is submitted and I would need to assign a new id to both the title of the round as well as the div where the result image would appear (since that changes with each new guess). But no matter what I try (generate all the code as string and append to body) the best I can get is 10 rounds but only functionality with round 1. All classes and Ids are same so the same event handlers and jQuery links should apply, yes?
Any-who, any help or suggestion is appreciated (thanks).
script code for creating rounds-
const nextRound = '
<div id="Round" class="level">
<h3 class="title"></h3>
<div class="buttondisplay">
<a id="boxa" class="button" class="active"></a>
<a id="boxb" class="button" class="active"></a>
<a id="boxc" class="button" class="active"></a>
<a id="boxd" class="button" class="active"></a>
</div><a id="submit" class="submitter">SUBMIT</a>
<div id="res1" class="results"></div>
</div>';
const buildRounds = () => { for (i = 1; i <= 10; i++){
$('#Gameboard').append(nextRound); }
}
The elements I would want to apply new ids are the boxa, boxb, boxc, and boxd, as well as the 'submit' and res1 (for resultsimage.png). Have been banging my head trying to make this work. Any help would be great. Or if you need more of the code, I can provide.
Thanks!
Rick

Issue
ids must be unique per page. You have a total of 6 different ids and each of those ids has 10 duplicates. jQuery/JavaScript will expect only one #Round or it expects one #boxc so once it finds an id it stops there and all the other duplicates are ignored or worse not entirely ignored. Basically when you have more than one element sharing an id, you will most likely get undesirable results if any at all. So you must make each id unique and a common way to do that is to assign a number to each id during a loop (i.e. "Round"+i)
BTW an element cannot have any duplicate attributes as well. So
<a id="boxd" class="button" class="active"></a>
is invalid and most likely the class='button' will be overwritten by class='active'. For multiple classes on a single element the syntax is:
<a id="boxd" class="button active"></a>
Details are commented in demo
Verify that all ids are unique by inspecting the page with DevTools.
Added Demo 2 which uses String Literals instead of Template Literals. IE does not support Template Literals hence Demo 2.
Demo 1 -- Using Template Literals
// Pass a number of rounds (i.e. 'qty')
function buildRounds(qty) {
/* Declare 'i' with 'let' limits i to only what
|| applies within the scope of the 'for' loop.
|| This limit helps 'i' to increment properly
|| without the influence of references outside
|| of the loop.
*/
for (let i = 1; i <= qty; i++) {
/* Use ES6 Template Literals* for complex strings
|| by wrapping the whole string with backticks `.
|| What is seen in code is literally rendered so
|| new lines are NOT ignored and escaping quotes
|| like this: `\'`or `\"` is not needed.
|| ${variable} is an interpolation of a value
|| inserted into the string. Note that the value 'i'
|| will be different on each loop therefore
|| ensuring unique ids.
*/
const nextRound = `
<div id="round${i}" class="level">
<h3 class="title">Round ${i}</h3>
<div class="buttonDisplay">
<a id="boxA${i}" class="button">A</a>
<a id="boxB${i}" class="button">B</a>
<a id="boxC${i}" class="button">C</a>
<a id="boxD${i}" class="button">D</a>
</div>
<a id="submit${i}" class="submitter">SUBMIT</a>
<div id="result${i}" class="results"></div>
</div>
`;
$('#gameBoard').append(nextRound);
}
/* Added function will toggle the '.active' class on/off
|| on each clicked '.button'. I don't know exactly what
|| good a "status" class would be if on every button,
|| so I changed it so now it can be toggled by user
*/
$('.button').on('click', function() {
$(this).toggleClass('active');
});
}
/* Call buildRounds() and pass the number
|| of rounds desired.
*/
buildRounds(10);
/* * Template Literals are not supported by M$ browsers
|| (i.e. IE). See Demo 2
*/
.button,
.submitter {
display: inline-block;
border: 3px ridge blue;
padding: 0 5px;
margin: 10px 5px;
color: blue;
cursor: pointer;
}
.button.active {
border: 3px inset red;
outline: 2px solid tomato;
color: red;
}
<main id='gameBoard'></main>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Demo 2 -- Using + to concat strings.
function buildRounds(qty) {
for (let i = 1; i <= qty; i++) {
/* This portion of the code is modifed to the use
|| of string literals instead of template literals.
|| If IE support is needed, then use this version.
*/
const nextRound = '<div id="round' + i + '" class ="level"><h3 class="title"> Round ' + i + ' </h3><div class="buttonDisplay"><a id="boxA' + i + '" class="button">A</a><a id="boxB' + i + '" class="button">B</a><a id="boxC' + i + '" class="button">C</a><a id="boxD' + i + '" class="button">D</a></div><a id="submit' + i + '" class="submitter">SUBMIT</a><div id="result' + i + '" class = "results"></div></div>';
$('#gameBoard').append(nextRound);
}
$('.button').on('click', function() {
$(this).toggleClass('active');
});
}
buildRounds(10);
.button,
.submitter {
display: inline-block;
border: 3px ridge blue;
padding: 0 5px;
margin: 10px 5px;
color: blue;
cursor: pointer;
}
.button.active {
border: 3px inset red;
outline: 2px solid tomato;
color: red;
}
<main id='gameBoard'></main>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

How to control tabIndex and focus trapping of elements w/o using positive values?

Maybe this is a bad idea entirely and am happy to take that as feedback; however, I'm looking for a good approach to controlling tabbable element on a document.
Requirements:
Api for controlling the tabbing order that is out of flow with DOM order.
Focus traps that COULD be cyclical and must be explicitly escaped.
A method to put non-tabbable elements in tab flow.
Written in Vanilla JS.
No positive tabIndex values.
Note: This is a personal thought experiment not really sure if this is a good idea or not. Will take any kind of leads: articles, libraries, code snippets but obviously the best would be an answer that meets all the requirements (or any other ideas you can muster up)!
button {
padding: 6px 8px;
margin: 12px;
}
.focus-trap{
padding: 24px;
background: rgba(150, 150, 48, .5);
}
.now-tabbable {
display: inline-flex;
padding: 15px 8px;
background: pink;
margin: 12px;
min-width: 45px;
justify-content: center;
}
<!--
.focus-traps:
- Are meant to declare that tab focus are order by the programmer.
- Focus-traps can be cyclical. currently denoted by class but up for any ideas data-attr etc.
.now-tabbable:
- Are meant to declare that this element is part of the tab flow.
-->
<div class="focus-trap">
<button>ZERO × escape</button>
<button>two</button>
<button>one</button>
<button>three</button>
<hr>
<div class="focus-trap cyclical">
<h1>Trap focus in here until escape</h1>
<button>four - B</button>
<button>four - C</button>
<button>four - A × escape</button>
</div>
<div class="now-tabbable">seven</div>
<div class="now-tabbable">five</div>
<div class="now-tabbable">six</div>
</div>
Generally speaking, you should avoid focus traps, except for modal contexts, like a modal dialogue.
The suggested solution here will instead establish a focus group with a roving tab index in which you can navigate focus by means of the arrow keys. TAB will then leave the group.
Keep in mind though, that you're using the focus group only for UI patterns where the behaviour can be expected. The ARIA standard mentions regarding Keyboard Navigation Inside Components:
[…] strongly advised to use the same key bindings as similar components in common GUI operating systems as demonstrated in § 3. Design Patterns and Widgets.
You might for example also format your buttons visually in a way that they clearly make one group, similar to a Toolbar.
class focusGroup {
constructor(el, cyclical: false) {
this.cyclical = cyclical;
// store children sorted by data-tabindex attribute
this.children = Array.from(el.querySelectorAll('[data-tabindex]')).sort((el1, el2) => el1.getAttribute('data-tabindex') > el2.getAttribute('data-tabindex'));
// remove tab index for all children
this.children.forEach(childEl => childEl.setAttribute('tabindex', '-1'));
// make first child tabbable
this.children[0].setAttribute('tabindex', '0');
this.i = 0;
// bind arrow keys
el.addEventListener('keyup', e => {
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') this.next();
if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') this.prev();
});
}
next() {
if (this.i < this.children.length -1) this.i += 1
else if (this.cyclical) this.i = 0;
this.updateFocus();
}
prev() {
if (this.i > 0) this.i -= 1
else if (this.cyclical) this.i = this.children.length -1;
this.updateFocus();
}
updateFocus() {
this.children.forEach(el => el.setAttribute('tabindex', '-1'));
this.children[this.i].setAttribute('tabindex', '0');
this.children[this.i].focus();
}
}
document.querySelectorAll('.focus-trap:not(.cyclical)').forEach(el => new focusGroup(el));
document.querySelectorAll('.focus-trap.cyclical').forEach(el => new focusGroup(el, true));
button {
padding: 6px 8px;
margin: 12px 0;
}
.focus-trap{
padding: 24px;
background: rgba(150, 150, 48, .5);
}
.now-tabbable {
display: inline-flex;
padding: 15px 8px;
background: pink;
margin: 12px;
min-width: 45px;
justify-content: center;
}
<!--
.focus-traps:
- Are meant to declare that tab focus are order by the programmer.
- Focus-traps can be cyclical. currently denoted by class but up for any ideas data-attr etc.
.now-tabbable:
- Are meant to declare that this element is part of the tab flow.
-->
<div class="focus-trap">
<button data-tabindex="0">ZERO × escape</button>
<button data-tabindex="2">two</button>
<button data-tabindex="1">one</button>
<button data-tabindex="3">three</button>
</div>
<div class="focus-trap cyclical">
<button data-tabindex>four - B</button>
<button data-tabindex>four - C</button>
<button data-tabindex>four - A × escape</button>
</div>
<div>
<button class="now-tabbable">seven</button>
<div class="now-tabbable">five</div>
<div class="now-tabbable">six</div>
</div>
Api for controlling the tabbing order that is out of flow with DOM order.
The visual order then will need to align with the focus order. In the example code, you can use data-tabindex="i" to control the
Focus traps that COULD be cyclical and must be explicitly escaped.
You can call the class and provide the second argument as true to establish a wrap over or cyclical order.
A method to put non-tabbable elements in tab flow.
Only elements with the attribute data-tabindex will be focussable.
No positive tabIndex values.
You'll need to use positive data-tabindexes, but the example code will always only use tabindex="0" to make one single element focussable. Meaning if you re-enter the group by means of tab, the last focussed element will again be focussed.

Change style of div with specific class?

I am truely sorry if this is a repeated question.
I want to set max-height of #menudd.show to satify my transition. I want to do this in javascript so i can specify the value more precisely.
This is what i got and its not working for some reason...
HTML:
<div id="menudd">
Home
About Me
Short-Term
Middle-Term
Long-Term
</div>
CSS:
#menudd {
position: fixed;
overflow: hidden;
max-height: 0px;
opacity: 0;
width: 200px;
border-radius: 10px;
box-shadow: 0 0 35px 15px black;
transition: all 1s ease;
}
#menudd.show {
opacity: 1;
}
JavaScript:
$("#menudd." + show).get(0).style.maxHeight = document.getElementById("menudd").getElementsByTagName("A")[0].offsetHeight * document.getElementById("menudd").getElementsByTagName("A").length + "px";
This outputs "show is not defined" in the console.
If i use $("#menudd.show").get(0).style.maxHeight it outputs "cannot read property 'style' of undefined" in the console.
If i use $("#menudd.show").style.maxHeight it outputs "cannot read property 'maxHeight' of undefined" in the console.
Any help is highly appreciated! Good day to you. :)
In your question, you said you wanted to look for an element with class "show" inside a div.
You are currently looking for a variable, and not a class. Change this:
$("#menudd." + show)
To this:
$("#menudd.show")
To set the max height, change it to this:
$( "#menuadd.show" ).css( { "maxHeight": document.getElementById("menudd").getElementsByTagName("A")[0].offsetHeight * document.getElementById("menudd").getElementsByTagName("A").length + "px" } );
First, I will add some stuff for readability - like a padding: 0.5em; into you dropdown menu. Also, the <br /> tag after the </a> tags (excluding the last one).
Second, the errors happened because there are no element in the page with the class show. I added it to the <div> of the dropdown to show it working and make the errors go away.
Remember this: if the console says there it is undefined, chances are that either you messed up in how to select the element OR it seriously doesn't exists because you misspelled a letter or let's say you forgot to add a class to a given tag (like in this case).
Third, your code in working condition is available below. It isn't using jQuery easing stuff but pure JavaScript - except by the onclick event that is better to use with jQuery:
$("#menu-dropdown").on("click", function() {
var menu_dropdown = document.getElementById("menudd");
var menu_item = menu_dropdown.getElementsByTagName("A");
$("#menudd").toggleClass("show");
menu_dropdown.style.maxHeight = menu_item[0].offsetHeight * menu_item.length + "px";
} );
#menudd {
padding: 0.5em;
position: fixed;
overflow: hidden;
max-height: 0px;
opacity: 0;
width: 200px;
border-radius: 10px;
box-shadow: 0 0 35px 15px black;
transition: all 1s ease;
}
#menudd.show {
opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<span id="menu-dropdown">DropDown</span>
<div id="menudd">
Home<br />
About Me<br />
Short-Term<br />
Middle-Term<br />
Long-Term<br />
</div>
You can view it working also on this video (Youtube) that I recorded.
Fourth, you can add classes, in pure JavaScript, easily. I won't write about it in full since there is a famous question with a godly answer by Peter Boughton already on Stack Overflow. Read it here.
Anyway, to add a class, simply call:
document.getElementById("MyElementID").className += " MyClass";
It is important to note the space. Since it is a string with values, you need to put it otherwise it will treat all the classes as one word (one class) that doesn't exists.
To remove:
document.getElementById("MyElementID").className = document.getElementById("MyElementID").className.replace( /(?:^|\s)MyClass(?!\S)/g , '' );
Explanation available through Peter's answer. So read it there (important)!
Finally, this is out of the scope of the question and merely a reminder: I need to state that if this is a dropdown menu, you will need to add the show class after some focus (or click even) by calling a function in a master menu of some sort and not by hardcoding it into the <div> like I did to show the code working. For example, a "Cars Brand" item of a menu would have a dropdown with the available car brands (Ford, Toyota and etc) after focusing on it.
So, it is wise to transform it in a function that receives the ID of the target dropdown to open. And do not forget to close it after losing focus (or whatever).

Using multiple selectors?

So I wrote this to take a button and recreate it as an Link with spans inside. However, I cant seem to get this to work for multiple buttons. I end up needing to copy and past the JS and enter in the different classes duplicating the entire script. There has to be an easier way to do this... Any thoughts?
Example of two buttons, and the only working solution thus far...
http://jsfiddle.net/En72J/5/
HTML
<div class="DIV_ONE">
<input type="button" class="INPUT_ONE" value="Today's Work Items 10" onclick="hAction_win1(document.win1,'CU_APPL_SUM_WRK_PERFORM_WEEKS', 0, 0, 'This Week\'s Items 10', false, true);" tabindex="16" name="CU_APPL_SUM_WRK_DATE_SEL_DAYS">
</div>
JQuery
// Page First loads Input Button Wrapped in Div.
// Grab Input Buttons Numbers ( Last 2 Characters )
var number = $('.INPUT_ONE').val().substr(-2);
// Grab Input Buttons Text, Minus the Numbers.
var term = $('.INPUT_ONE').val().slice(0, -2);
// Grab Input Buttons OnClick Value
var script = $('.INPUT_ONE').attr("onclick");
// Append 'term' Float Left
$('.DIV_ONE').append('<span class="text">' + term + '</span>');
// Append 'number' Float Right
$('.DIV_ONE').append('<span class="number">' + number + '</span>');
// Wrap Both 'term' and 'number' in an <A> LINK and set OnClick with 'script' var.
var second = $('.DIV_ONE').wrapInner('');
// Finally, Delete old Button. New <A> Link as Victor!
$('.INPUT_ONE').remove();
CSS
.btn_style {
border-bottom: 1px dotted #CCCCCC;
color: #666666;
display: block;
font-family: verdana;
font-size: 12px;
overflow: auto;
text-decoration: none;
}
.number {
background: none repeat scroll 0 0 #72716E;
color: #FFFFFF;
display: block;
float: right;
font-weight: bold;
padding: 4px;
position: relative;
width: 20px;
}
.text {
float: left;
padding: 4px;
}
Consider using a second class name to identify the elements you wish to process, then loop through them like so:
<div class="DIV_ONE buttonMe">
<input type="button" class="INPUT_ONE" value="Today's Work Items 10" onclick="hAction_win1(document.win1,'CU_APPL_SUM_WRK_PERFORM_WEEKS', 0, 0, 'This Week\'s Items 10', false, true);" tabindex="16" name="CU_APPL_SUM_WRK_DATE_SEL_DAYS">
</div>
JS:
$('.buttonMe').each(function() {
current= $(this);
// at this point "current" points to the outer DIV
currentInput = $(this).find('input')
// then you can manipulate the current input
})
Then you can treat "currentInput" as if it were the hard-coded element reference you're currently using in your code.
A simple loop would solve that :
$('input[class^="INPUT_"]').each(function() {
var n = $('<span />', {'class':'number', text : this.value.slice(-2)}),
t = $('<span />', {'class':'text', text : this.value.slice(0,-2)}),
a = $('<a />', {'class':'btn_style', onclick : $(this).attr('onclick')});
$(this).closest('div').append(a.append(n,t)).end().remove();
});
FIDDLE
You could select all inputs of type button by using
$('input:button').each( function(index) {
//do work here
});
and go through each button on your page.
create a seperate function and call the function with a selector for any number of inputs and div
function createlink(input, div) {
// Page First loads Input Button Wrapped in Div.
// Grab Input Buttons Numbers ( Last 2 Characters )
var number = $(input).val().substr(-2);
// Grab Input Buttons Text, Minus the Numbers.
var term = $(input).val().slice(0, -2);
// Grab Input Buttons OnClick Value
var script = $(input).attr("onclick");
// Append 'term' Float Left
$(div).append('<span class="text">' + term + '</span>');
// Append 'number' Float Right
$(div).append('<span class="number">' + number + '</span>');
// Wrap Both 'term' and 'number' in an <A> LINK and set OnClick with 'script' var.
var second = $(div).wrapInner('');
// Finally, Delete old Button. New <A> Link as Victor!
$(input).remove();
}
createlink('.INPUT_ONE', '.DIV_ONE');
createlink('.INPUT_TWO', '.DIV_TWO');
fiddle here
Use JQuery's $(this) and JQuery's .each.
Here's a working fiddle: http://jsfiddle.net/4jqBj/
HTML:
<div class="item">
<input type="button" class="item_btn" value="Today's Work Items 10" tabindex="16" />
</div>
<div class="item">
<input type="button" class="item_btn" value="This Week's Items 22" tabindex="16" />
</div>
JQUERY:
$(".item_btn").each(function () {
var number = $(this).val().substr(-2);
var term = $(this).val().slice(0, -2);
$(this).parent().append('<span class="text">' + term + '</span>').append('<span class="number">' + number + '</span>');
$(this).parent().wrapInner('');
$(this).remove();
});

Add to Favorites Array

What I want to do in Javascript/Jquery is be able to click a button (a button on each item), that adds it to an array. This array will then be posted in order when you click on a favorites page.
I'm just having a hard time wrapping my head around how this would work. Because I may want each item in the array to contain a few things, such as a picture and text describing the item.
In general terms/examples, how would this be set up?
There are a number of ways to do this. But, I'll go with one that's a bit more general - which you can extend for yourself:
http://jsfiddle.net/TEELr/11/
HTML:
This simply creates different elements with the favorite class - which will be the selector by which we check if an element has been clicked.
<div class="favorite"><p>Add to favorites</p></div>
<div class="favorite type2"><p>Just another favorite type</p></div>
<button id="reveal">
Reveal Favorites
</button>
JS:
Every time an element with the "favorite" CSS class is clicked, it is added to the array - this also works for elements with more than one class (that have the "favorite" CSS class).
Now, when the "Reveal Favorites" button is clicked, it will alert what's in the array - which is in the order clicked (as asked).
$(document).ready(function() {
var favorites = [];
var counter = 0;
$('.favorite').click(function() {
++counter;
favorites.push("\"" + $(this).text() + " " + counter + "\"");
});
$('#reveal').click(function() {
alert(favorites);
});
});
CSS:
Simple CSS that only exist for demonstration purposes to prove previous point with multiple CSS class selectors:
.favorite {
width: 400px;
height: 50px;
line-height: 50px;
text-align: center;
display: block;
background-color: #f3f3f3;
border-bottom: 1px solid #ccc;
}
.favorite.type2 {
background-color: #ff3;
}
.favorite:hover {
cursor:hand;
cursor: pointer;
}

CSS doesn't effect dynamically created list on Phonegap

similar to my question is this one
and this one
however, still haven't sorted any solution..
i have in the <body> an unordered list <ul> each element of which is added after performing a query to a database..So according to the result, i have the corresponding <li> tags created..
Ther problem is, that although i have some css to seperate <li> to "odd" and "even" so that i can apply some different styles, and i have confirmed, that the "odd" and even" attibute has passed as an attribute (classname) to <li> still the corresponding css rules, do not apply
here is some of my code.. first the html part
<div id="sqldiv">
<ul class="test" id="attempt">
</ul>
</div>
and the javascript part..
for (var i=0; i<len; i++){
var somevariable = results.rows.item(i).dbvariable;
if (i%2==0)
lt='Even';
else
lt='Odd';
var newLI = document.createElement("LI");
newLI.className = lt;
var htmlcontainer = ("<div>my text :" + my variables + "</div><div>my text :</div><div>" my variables + "</div>");
newLI.innerHTML = htmlcontainer ;
var gl = document.getElementById("attempt");
gl.appendChild(newLI);
}
and the css
li {
background: #8258FA;
list-style-type:none;
list-style-image:none;
margin: 5px 5px;
border-radius: 15px;
}
li.odd {
border-bottom :1px dotted #ccc;
list-style-type:none;
list-style-image:none;
margin: 5px 5px;
background: #000000;
}
the li styling does apply, but the styling by class (li.odd) doesn't
after spending last 24 hours searching..i tried sth that didn't cross my mind..!!!!
in javascript i name class Even and Odd..and on css i have .even and .odd rules.. and this worked great on another project..!!!
but, for some reason, in this case there seemed to be a "case sensitive" issue.. since i changed css rule to .Even and .Odd, they were applied successfully..
before reaching this, i had also tried assigning dynamic css rules using jquery..and after some attempts, i ended up to the case sensitive..

Categories

Resources