Get the child element and modify the text - javascript

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 == '[+]' ? '[-]' : '[+]';
}

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");

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 deactivate a div as dropzone

I have three divs with a title and an image in an other, bigger div. I'm trying to drag and drop them around, but if I drop it above an other div they drop in each other. How can I disable de divs with the image as a dropzone?
I worked with JavaScript, HTML and css.
<div class=dirFiles>
<div draggable='true' onmousedown='ablegenVerbieten()' class='file'>
<p>Title</p>
<img></img>
</div>
<div draggable='true' onmousedown='ablegenVerbieten()' class='file'>
<p>Title</p>
<img></img>
</div>
<div draggable='true' onmousedown='ablegenVerbieten()' class='file'>
<p>Title</p>
<img></img>
</div>
</div>
<script>
function ziehen(ev) {
ev.dataTransfer.setData('text', ev.target.id);
}
function ablegenErlauben(ev) {
ev.preventDefault();
}
function ablegenVerbieten(ev) {
oevent.preventDefault ? event.preventDefault() : event.returnValue = false;
}
function ablegen(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData('text');
var target = ev.target;
while (" "+target.className+" ".indexOf(" dirFiles ") == -1) target = target.parentNode;
target.appendChild(document.getElementById(data));
}
window.addEventListener("load",function () {
var elms = document.querySelectorAll(".dirFiles");
for (var i = 0; i < elms.length; i++) {
var dirFiles = elms[i];
dirFiles.addEventListener("drop",ablegen);
dirFiles.addEventListener("dragover",ablegenErlauben);
};
elms = document.querySelectorAll("[draggable=true]")
for (var i = 0; i < elms.length; i++) {
var draggable = elms[i];
draggable.addEventListener("dragstart",ziehen);
};
});
</script>

Add and Remove class to click a dynamic Button

Trying to Add and Remove class to click dynamic Buttons, means this button <button class="one"></button> get class dynamically like this: <button class="one text1">text1</button>
So if button one has class .text1 and by click this add class .hide to list item <li class="text1"> like <li class="text1 show">
Same for button two <button class="two"></button> and by click add class <li class="text2 show">
Note: when click button two, then should remove class .show and add new class .hideto button one.
Main HTML:
<div id="main-id">
<button class="one"></button>
<button class="two"></button>
<ul>
<li>
<!--List 1-->
<div class="label">
text1
</div>
</li>
<li>
<!--List 2 is Same-->
<div class="label">
text1
</div>
</li>
<li>
<!--List 3 is different-->
<div class="label">
text2
</div>
</li>
</ul>
</div>
Script:
$('.label a').each(function() {
var $this=$(this);
$this.closest('li').addClass($this.text());
});
// Combine This
$('button').each(function(){
var liInd = 0;
var cl = '';
var txt = '';
var clses = [];
var ind = $('button').index($(this)) + 1;
$('li').each(function(){
if(clses.indexOf($(this).attr('class')) === -1){
clses.push($(this).attr('class'));
liInd = liInd + 1;
}
if(ind === liInd){
cl = $(this).attr('class');
txt = $(this).find('a').text();
return false; //break
}
});
$('button:nth-child(' + ind + ')').addClass(cl);
$('button:nth-child(' + ind + ')').text(txt);
});
See Example on Fiddle
I have tried this by add/remove class by click function, but problem is Buttons get class dynamically from List items, so I'm not able to target button.
Any suggestion for other way to do this by JS/ Jquery?
Here is an alternative solution
$('button').each(function () {
var liInd = 0;
var cl = '';
var txt = '';
var clses = [];
var ind = $('button').index($(this)) + 1;
$('li').each(function () {
if (clses.indexOf($(this).attr('class')) === -1) {
clses.push($(this).attr('class'));
liInd = liInd + 1;
}
if (ind === liInd) {
cl = $(this).attr('class');
txt = $(this).find('a').text();
return false; //break
}
});
if (txt != '') {
$('button:nth-child(' + ind + ')').addClass(cl);
$('button:nth-child(' + ind + ')').text(txt);
}
});
$('button').click(function () {
if ($(this).attr('class')[0] == 'all') {
showAll();
return false; // end this function
}
var allCls = $(this).attr('class').split(' ');
$('li').each(function () {
if (allCls.indexOf($(this).find('a').text()) > -1) {
$(this).closest('li').removeClass('show').addClass('hide');
} else {
$(this).closest('li').removeClass('hide').addClass('show');
}
});
});
function showAll() {
$('li').removeClass('hide').addClass('show');
}
Fiddle: https://jsfiddle.net/taleebanwar/yaLm4euk/13/
DEMO
$('.label a').each(function () {
var $this = $(this);
$this.closest('li').addClass($this.text());
});
// Combine This
$('button').each(function () {
var liInd = 0;
var cl = '';
var txt = '';
var clses = [];
var ind = $('button').index($(this)) + 1;
$('li').each(function () {
if (clses.indexOf($(this).attr('class')) === -1) {
clses.push($(this).attr('class'));
liInd = liInd + 1;
}
if (ind === liInd) {
cl = $(this).attr('class');
txt = $(this).find('a').text();
return false; //break
}
});
$('button:nth-child(' + ind + ')').addClass(cl);
$('button:nth-child(' + ind + ')').text(txt);
});
$(document).on('click', 'button',function(e){
var textClass = $.grep(this.className.split(" "), function(v, i){
return v.indexOf('text') === 0;
}).join();
console.log(textClass);
$('li').removeClass('show').addClass('hide')
$('li').each(function(){
if($(this).hasClass($.trim(textClass))){
$(this).removeClass('hide').addClass('show');
} else {
$(this).removeClass('show').addClass('hide');
}
})
})
.show{display:list-item;}
.hide{display:none;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="main-id">
<button class="one"></button>
<button class="two"></button>
<ul>
<li>
<!--List 1-->
<div class="label">
text1
</div>
</li>
<li>
<!--List 2 is Same-->
<div class="label">
text1
</div>
</li>
<li>
<!--List 3 is different-->
<div class="label">
text2
</div>
</li>
</ul>
</div>

Hiding specific elements after creating them with ng-repeat

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.

Categories

Resources