Hiding specific elements after creating them with ng-repeat - javascript

I'm trying to create a piano on my page using two image files: white-key and black-key.
I've created them, but the black keys on the piano alternate in groups of 2 and 3 and I'd hide the black key images for indices [1, 4(1+3), 8(4+4), 11(8+3), 15(11+4), .., 54]. I'm unsure of how to go about doing this though.
This is how I created them.
HTML:
<div ng-controller="DrawCtrl as draw">
<ul class="white-keys">
<li ng-repeat="t in draw.range(56) track by $index">
<img ng-src={{draw.white_key}} />
</li>
</ul>
<ul class="black-keys">
<li ng-repeat="t in draw.range(55) track by $index">
<img ng-src={{draw.black_key}} />
</li>
</ul>
</div>
JS:
angular.module('app')
.controller('DrawCtrl', function() {
var self = this;
self.piano_back = 'img/background.png';
self.white_key = 'img/midi_white_up.png';
self.black_key = 'img/midi_black_up.png';
self.range = function(num) {
return new Array(num);
};
});
EDIT: Got it working thanks to hansmaad's answer.
HTML:
<ul class="black-keys">
<li ng-repeat="key in draw.keys" ng-switch="key.black">
<img ng-switch-when="true" ng-src={{draw.black_key}} />
<img ng-switch-when="false" ng-src={{draw.black_key}} class="black-hidden" />
</li>
</ul>
JS:
self.keys = [];
var keyGroupOf3 = true;
self.keys.push({black: true}); // first key is shown
var i = 1;
while (i < 54) {
self.keys.push({black: false});
// alwasy followed by at least two
self.keys.push({black: true});
self.keys.push({black: true});
if (keyGroupOf3){
self.keys.push({black: true});
i += 4;
} else {
i += 3;
}

I think you should create your keyboard in the controller as array of keys. You can then use a single ng-repeat to draw all the keys. To draw the right img for a key you can use ng-switch or store the imgage url in the key.
A simple example without images but using ng-class:
http://plnkr.co/edit/kIvRqdkbHHNcUXKzSLZC?p=preview
<div ng-controller="DrawCtrl as draw">
<ul >
<li ng-repeat="key in draw.keys" ng-class="{ 'black' : key.black, 'white' : !key.black}">
</li>
</ul>
</div>
Controller:
function DrawCtrl() {
this.keys = []
for(var i = 0, e = 55; i < e; ++i) {
this.keys.push({
black : isBlackKey(i)
});
}
function isBlackKey(i) {
// your piano logic here
return i % 2 == 0;
}
}
Using ng-switch you could do:
<ul>
<li ng-repeat="key in draw.keys" ng-switch="key.black">
<span ng-switch-when="true">Black key</span>
<span ng-switch-when="false">White key</span>
</li>
</ul>
Edit: This could be a simple, stupid algorithm to fill the keys array:
this.keys = []
var lastWasBlack = true;
var d = 5;
var next = 5;
for(var i = 0, e = 55; i < e; ++i) {
var isBlack = !lastWasBlack;
if (i === next) {
isBlack =!isBlack;
d = d === 5 ? 7 : 5;
next += d;
}
this.keys.push({
black : isBlack
});
lastWasBlack = isBlack;
}
}
http://plnkr.co/edit/kIvRqdkbHHNcUXKzSLZC?p=preview

Have a look at the below plunker. I haven't added any css. But just to hide and show the images you can use this link.
http://plnkr.co/edit/pgFKHShXpeS4EQhoaFTI?p=preview
<ul class="white-keys">
<li ng-repeat="t in draw.range(20) track by $index">
<img ng-src='http://lorempixel.com/g/50/15/technics' ng-hide="($index == 1 || $index == 4 || $index == 8 || $index == 11)"/>
</li>
</ul>
I have used ng-hide to hide the images at specific position.
Is this you are looking for? If not let me know.

Related

Javascript Nav with dropdown click event issue

I have a doropdown menu that should work when used/in view.
I would like to have a function that works only when the dropdown is used.
So Ideally navigation with dropdown and nav without.
the following fuctions will be added if dropdown exists.
The Click event doesn't seems to fire at all.
My assumption is that
const navLinks = dropDownBlock[i].querySelectorAll(".nav-items > li");
is't correct! or is not fired
I wan't to use as much as possible es6, and I wan't to make sure everything is reusable and works only when in view.
Demo here:
const dropDownBlock = document.querySelectorAll(".has-nav-panel");
let result = true;
for (let i = 0; i < dropDownBlock.length; i++) {
if (dropDownBlock[i] <= 0) {
result = false;
break;
}
const navLinks = dropDownBlock[i].querySelectorAll(".nav-items > li");
const dropdownCta = dropDownBlock[i].querySelector(".nav-panel-cta");
const dropdownPanel = dropDownBlock[i].querySelector(".nav-panel");
//-------------------------------------
//Doesn it have dropdown
//-------------------------------------
Array.prototype.forEach.call(navLinks, function (el, i) {
let currentNavLink = navLinks[i];
currentNavLink.addEventListener("click", function () {
console.log('click');
megaNavClickAndTouchHandler(navLinks, currentNavLink);
});
megaNavResetOnBreakPoint(navLinks, currentNavLink, mediaQuery);
});
function megaNavResetOnBreakPoint(elements, currentElement, mqNav) {
if (matchMedia) {
let navigationBar = currentElement.closest(".header");
let navigationItems = currentElement.closest(".nav-wrap");
mqNav.addListener(function () {
if (mqNav.matches) {
document.querySelectorAll("body")[0].classList.remove("is-no-scroll");
navigationBar.classList.remove("is-active");
navigationItems.classList.remove("is-active");
//navigationBar.querySelectorAll(".burger")[0].classList.remove("is-active");
megaNavClosePanels(elements);
} else {
megaNavClosePanels(elements);
}
});
}
}
function megaNavClickAndTouchHandler(elements, currentElement) {
let isSubNavLink = currentElement.classList.contains("has-nav-panel");
let isSubNavLinkActive = currentElement.classList.contains("is-active");
let navBarContainer = currentElement.closest(".header");
if (!isSubNavLink) {
window.location = currentElement.firstElementChild.getAttribute("href");
} else if (isSubNavLink && !isSubNavLinkActive) {
megaNavClosePanels(elements);
currentElement.classList.add("is-active");
dropdownCta.setAttribute("aria-expanded", true);
dropdownPanel.ariaHidden = "false";
} else {
megaNavClosePanels(elements);
}
}
function megaNavClosePanels(elements) {
for (let j = 0; j < elements.length; j++) {
if (elements[j].classList.contains("has-nav-panel")) {
elements[j].classList.remove("is-active");
dropdownCta.setAttribute("aria-expanded", false);
dropdownPanel.ariaHidden = "true";
}
}
}
//-------------------------------------
// end dropdown functions
//-------------------------------------
}
<header role="banner" class="header">
<div class="container-fluid g-0 bg-black">
<nav id="main-navigation" class="navigation">
<div class="container has-px-0 has-px-md-4">
<div class="nav-wrap" aria-label="main navigation">
<ul class="nav-items list-unstyled">
<li class="is-d-lg-flex active"><a class="nav-link" href="#">nav item</a></li>
<li class="is-d-lg-flex "><a class="nav-link" href="#">nav item</a></li>
<li class="is-d-lg-flex"><a class="nav-link" href="#">nav item</a></li>
<li class="has-nav-panel is-d-lg-flex is-align-center">
<button role="button" aria-expanded="false" class="nav-panel-cta has-pb-3 has-pt-3 has-px-4 has-p-lg-0"><span class="has-mr-2">Dropdown</span></button>
<div class="nav-panel" aria-hidden="true">
<div class="row no-gutters">
<div class="col-md-12">
<ul class="list-unstyled has-pl-3 has-pl-lg-0">
<li>nav item</li>
<li>nav item</li>
</ul>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</nav>
</div>
</header>
your html structure
<ul class="nav-items list-unstyled">
<li class="has-nav-panel is-d-lg-flex is-align-center">
but your script select the child then the parent
const dropDownBlock = document.querySelectorAll(".has-nav-panel");
.....
const navLinks = dropDownBlock[i].querySelectorAll(".nav-items > li");
I think you don't need to loop and here fix, replace
const dropDownBlock = document.querySelectorAll(".has-nav-panel");
let result = true;
for (let i = 0; i < dropDownBlock.length; i++) {
if (dropDownBlock[i] <= 0) {
result = false;
break;
}
const navLinks = dropDownBlock[i].querySelectorAll(".nav-items > li");
const dropdownCta = dropDownBlock[i].querySelector(".nav-panel-cta");
const dropdownPanel = dropDownBlock[i].querySelector(".nav-panel");
with
let result = true;
dropDown();
function dropDown() {
let dropDownBlock = document.querySelectorAll(".nav-items")
const navLinks = dropDownBlock[0].querySelectorAll(".has-nav-panel");
if (navLinks .length <= 0) {
result = false;
return;
}
const dropdownCta = dropDownBlock[0].querySelector(".nav-panel-cta");
const dropdownPanel = dropDownBlock[0].querySelector(".nav-panel");

How to capture and display data from a list, without having to create functions for each one?

I have the following problem: I want that every time that one of the elements of the list is clicked, the data-link value is printed, if I need to create a function for each of the elements of the list.
<ul id="programs">
<li id="design" data-link="www.monster.inc">Diseño</li>
<li id="fashion" data-link="www.iceage.inc">Moda</li>
<li id="management" data-link="www.transilvania.inc">Management</li>
<li id="arts" data-link="www.ralph.inc">Artes</li>
</ul>
<p id="demo"></p>
<script>
var one = document.getElementById("design");
one.onclick = function() {
var x = one.getAttribute("data-link");
document.getElementById("demo").innerHTML = x;
}
var two = document.getElementById("fashion");
two.onclick = function() {
var y = two.getAttribute("data-link");
document.getElementById("demo").innerHTML = y;
}
var three = document.getElementById("management");
three.onclick = function() {
var z = three.getAttribute("data-link");
document.getElementById("demo").innerHTML = z;
}
var four = document.getElementById("arts");
four.onclick = function() {
var w = four.getAttribute("data-link");
document.getElementById("demo").innerHTML = w;
}
</script>
I can think of two selectors for the above structure.
var links = document.querySelectorAll('[data-link]');
and
var links = document.querySelectorAll('#programs > li');
And then loop over the NodeList and attach the function to onclick prop. I recommend addEventListener, however.
var links = document.querySelectorAll('[data-link]'),
demo = document.getElementById("demo");
links.forEach(function(link){
link.onclick = function() {
var x = this.getAttribute("data-link");
demo.innerHTML = x;
}
});
<ul id="programs">
<li id="design" data-link="www.monster.inc">Diseño</li>
<li id="fashion" data-link="www.iceage.inc">Moda</li>
<li id="management" data-link="www.transilvania.inc">Management</li>
<li id="arts" data-link="www.ralph.inc">Artes</li>
</ul>
<p id="demo"></p>
There are a number of solutions for this one, with or without jQuery.
Here's a simple one that only updates your one list and not other broadly selected elements on the page.
var list = document.getElementById("programs").getElementsByTagName("li")
for (var i=0;i<list.length;i++)
{
list[i].onclick = function() {
var x = this.getAttribute("data-link");
document.getElementById("demo").innerHTML = x;
}
}
You can attach the onclick event to the parent ul element and use the event.target property to access the child element on which the click happened.
var programs = document.getElementById("programs");
programs.onclick = function(e) {
var link = e.target.getAttribute("data-link");
if (link)
document.getElementById("demo").innerHTML = link;
};
<ul id="programs">
<li id="design" data-link="www.monster.inc">Diseño</li>
<li id="fashion" data-link="www.iceage.inc">Moda</li>
<li id="management" data-link="www.transilvania.inc">Management</li>
<li id="arts" data-link="www.ralph.inc">Artes</li>
</ul>
<p id="demo"></p>
You need not iterate through each li and attach onclick events to them individually.

Traversing the DOM, getting text with highest appearance and add a class to the parent

I am traversing this DOM:
<ul>
<li class="item">
<span class="category">
most occurring text
</span>
</li>
<li class="item">
<span class="category">
some text
</span>
</li>
<li class="item">
<span class="category">
some text
</span>
</li>
<li class="item">
<span class="category">
most occurring text
</span>
</li>
<li class="item">
<span class="category">
most occurring text
</span>
</li>
</ul>
With the following code:
var myNodelist = Array.from(document.querySelectorAll(".category"));
var obj = {};
for(var i = 0; i < myNodelist.length; i++){
//convert array to object with unique elements and number of times
each element is repeated
var x = myNodelist[i].innerHTML;
//console.log(x);
if(!obj[x]){
obj[x] = 1;
} else {
obj[x]++;
}
}
var index = 0;
var max = 0;
for(var obIndex in obj) {
// Traverse the object to get the element
if(obj[obIndex] > max) {
max = obj[obIndex];
index = obIndex.replace(" ", "");
}
}
console.log(index + " is max time repeated: " + max + " times." );
var v = document.getElementsByClassName("category");
for(var m = 0; m < myNodelist.length; m++) {
var subText = myNodelist[m].childNodes;
var len = subText.length;
for (var jj = 0; jj < len; jj++) {
if(subText[jj].nodeType === Node.TEXT_NODE) {
console.log(subText[jj].nodeValue);
subText[jj].nodeValue =
subText[jj].nodeValue.replace(/Mock/,"123");
}
}
}
Currently i am getting the index successfully with the value from the exertion of the highest text appearance in the DOM. Then i am looping through the Nodelist again, evaluating if its a,
Node.TEXT_NODE
https://developer.mozilla.org/de/docs/Web/API/Node/nodeType
Now i only know how to replace the
textNode.value
with another value.
What i am really trying to achieve is to get the parentNode of the textNode and add a class to it. If the condition for index (highest appearance) is met. What i found is
Adding a class to a given element. and
ParentNode MDN
The problem is i can't really figure out how to
access the parentNode out of the second for loop and add a class to
the parentNode, so all parents (span tags) that only have the index (text
value) get a certain class.
Thanks for help !
You can access to li using parentNode on the myNodelist[m]
var myNodelist = Array.from(document.querySelectorAll(".category"));
var obj = {};
for(var i = 0; i < myNodelist.length; i++){
//convert array to object with unique elements and number of times
//each element is repeated
var x = myNodelist[i].innerHTML;
//console.log(x);
if(!obj[x]){
obj[x] = 1;
} else {
obj[x]++;
}
}
var index = 0;
var max = 0;
for(var obIndex in obj) {
// Traverse the object to get the element
if(obj[obIndex] > max) {
max = obj[obIndex];
index = obIndex.replace(" ", "");
}
}
console.log(index + " is max time repeated: " + max + " times." );
var v = document.getElementsByClassName("category");
for(var m = 0; m < myNodelist.length; m++) {
var subText = myNodelist[m].childNodes;
var len = subText.length;
for (var jj = 0; jj < len; jj++) {
if(subText[jj].nodeType === Node.TEXT_NODE) {
if (obj[subText[jj].nodeValue] == max) {
myNodelist[m].parentNode.className += " red";
}
console.log(subText[jj].nodeValue);
subText[jj].nodeValue =
subText[jj].nodeValue.replace(/Mock/,"123");
}
}
}
.red {
color: red;
}
<ul>
<li class="item">
<span class="category">
most occurring text
</span>
</li>
<li class="item">
<span class="category">
some text
</span>
</li>
<li class="item">
<span class="category">
some text
</span>
</li>
<li class="item">
<span class="category">
most occurring text
</span>
</li>
<li class="item">
<span class="category">
most occurring text
</span>
</li>
</ul>
What i am really trying to achieve is to get the parentNode of the
textNode and add a class to it.
You don't need to find it, you already have myNodelist[m] whose childNodes you were iterating.
If the condition for index (highest appearance) is met.
You have the node-Value with you here subText[jj].nodeValue, and you already have the obj having the number of appearances by the nodeValue
So, simply add this logic
if ( obj[ subText[jj].nodeValue ] == max )
{
//logic to add the class should be here
myNodelist[m].classList.add("otherclass");
}
subText[jj].nodeValue = subText[jj].nodeValue.replace(/Mock/,"123");
To get the parent of a text node just use myTextNode.parentNode and then use classList to add the class to the parent. This could also be achieved using the treewalker api.
function markMostOccurring(parentSelector, markFn) {
var parent = document.querySelector(parentSelector) || document.body;
var walker = document.createTreeWalker(parent, NodeFilter.SHOW_TEXT, {
acceptNode: node => !!node.nodeValue.trim()
});
var occurenceMap = {};
while(walker.nextNode()) {
var key = walker.currentNode.nodeValue.trim();
var nodes = occurenceMap[key] = occurenceMap[key] || [];
nodes.push(walker.currentNode);
}
var nodes = Object.keys(occurenceMap)
.sort((a, b) => occurenceMap[b].length - occurenceMap[a].length)
.map(key => occurenceMap[key])[0]
.forEach(node => markFn.call(node));
}
markMostOccurring('.container', function() {
this.parentNode.classList.add('mark');
this.nodeValue = this.nodeValue.replace('most', 'cat');
});
.mark {
color: red;
}
<ul class="container">
<li class="item">
<span class="category">
most occurring text
</span>
</li>
<li class="item">
<span class="category">
some text
</span>
</li>
<li class="item">
<span class="category">
some text
</span>
</li>
<li class="item">
<span class="category">
most occurring text
</span>
</li>
<li class="item">
<span class="category">
most occurring text
</span>
</li>
</ul>

How to get the maximum value from the HTML attributes

My HTML looks like:
<ul>
<li data-target="12">1</li>
<li data-target="4">2</li>
<li data-target="9">3</li>
<li data-target="15">4</li>
<li data-target="23">5</li>
<li data-target="32">6</li>
<li data-target="7">7</li>
<li data-target="10">8</li>
<li data-target="5">9</li>
<li data-target="2">10</li>
</ul>
What method should I use to get the maximum value from the data-target attributes? Do vanilla JavaScript or jQuery have some native functions for this or I should just use for loop to iterate through all the items and get the maximum value, shouldn't I?
One way (without bothering with an array);
var max = 0;
$("li[data-target]").each(function() {
max = Math.max(max, parseInt($(this).data("target"), 10));
});
alert(max);
use Math.max.apply() method to get max value from a numeric array.
var arr = $('li[data-target]').map(function(){
return $(this).data('target')
});
console.log(Math.max.apply(Math,arr));
Fiddle Demo
Try this: use .map() along with Math function:
var targets = $("li").map(function() {
return $(this).data("target");
}).get();
var max = Math.max.apply(Math,targets);
Demo
This should work...
var array = [];
$('li').each(function() {
array.push($(this).data('target'));
});
var maxNumber = Math.max.apply(Math, array);
alert(maxNumber);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<li data-target="12">1</li>
<li data-target="4">2</li>
<li data-target="9">3</li>
<li data-target="15">4</li>
<li data-target="23">5</li>
<li data-target="32">6</li>
<li data-target="7">7</li>
<li data-target="10">8</li>
<li data-target="5">9</li>
<li data-target="2">10</li>
var maxVal = 0;
$('[data-target]').each(
function(){
if($(this).attr('data-target') > maxVal){
maxVal = $(this).attr('data-target');
}
});
fiddle
Try with this:
var maxValue = 0;
$("li").each(function(index,val){
var value = $(this).attr('data-target');
if(value > maxValue) maxValue= value;
});
Yes you can get max value using for each loop of jquery. For each value of li get its attribute data-target. e.g
var maxVal=0;
$(this).find('li').each(function(){
// cache jquery var
var current = $(this);
var val=parseInt(current.attr( "data-target" ));
if(val > maxVal){
maxVal=val;
}
});
console.log(maxVal);//Its the max value
Turak Vladyslav
below code will work you can check it once
var Lis = $('#test').find('li');
var dataArray = []
for(var i=0;i<Lis.length;i++){
dataArray.push($(Lis[i]).attr('data-target'))
}
var maxDatatTarget = Math.max.apply(null, dataArray)
alert("max data target value"+maxDatatTarget)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="test">
<li data-target="12">1</li>
<li data-target="4">2</li>
<li data-target="9">3</li>
<li data-target="15">4</li>
<li data-target="23">5</li>
<li data-target="32">6</li>
<li data-target="7">7</li>
<li data-target="10">8</li>
<li data-target="5">9</li>
<li data-target="2">10</li>
</ul>
Try this if it works for you :
function calculateMaxTarget(){
var attr[] = $('li').attr('data-target');
var max = 0;
for (var i=0; i < attr.length; i++) {
if(attr[i]>max){
max = attr[i];
}
};
return max;
}

Get the child element and modify the text

I need to toggle the text in the span element from [+] to [-] when the button is clicked with JavaScript. How can I achieve this with my current toggle function?
HTML
...
<ul>
...
<li>
<ul>
<h3>header</h3>
url-link
<p>description</p>
<ul>
<div class="department" style="display: none;">
<ul class="placemark"> ... </ul>
<ul class="contact"> ... </ul>
</div>
<button class="department-collapse" onclick="toggle(this)">
<span>[+]</span> Contact Information
</button>
</li>
...
<ul>
...
JavaScript
function toggle(self) {
var n = self.parentNode.childNodes;
for(var i = 0; i < n.length; i++) {
if (n[i].className == 'department') {
n[i].style.display = n[i].style.display == '' ? 'none' : '';
}
}
}
Inside your function, verify your span (JS code):
function toggle(self) {
var n = self.parentNode.childNodes;
for(var i = 0; i < n.length; i++) {
if (n[i].className == 'department') {
n[i].style.display = n[i].style.display == '' ? 'none' : '';
}
}
var mySpan = self.childNodes[0];
mySpan.innerHTML = mySpan.innerHTML == '[+]' ? '[-]' : '[+]';
}

Categories

Resources