Targeting the closest class in javascript - javascript

I'm just learning javascript and ran into a problem.
I'm trying to target the closest class and change the value, but I just keep getting Cannot read property 'innerText' of null at HTMLUListElement.
Is this not possible or is there another way I should look into?
Any help or guidance is appreciated.
<div id="someId">
<h2>Test</h2>
<ul>
<li>
<p class="amount">5</p>
<span class="name">My Name</span>
<button class="plus">Plus</button>
</li>
<li>
<p class="amount">10</p>
<span class="name">My Name 2</span>
<button class="plus">Plus</button>
</li>
<li>
<p class="amount">15</p>
<span class="name">My Name 3</span>
<button class="plus">Plus</button>
</li>
</ul>
const someList = document.querySelector('#someId ul');
someList.addEventListener('click', function(e) {
if (e.target.className === 'plus') {
let newValue = e.target.closest('.amount');
newValue.innerText += "5";
}
});
I've changed the .amount to lots of different things, p and li > p etc but no luck.

.closest() goes up the dom/xpath looking at ancestors. your p.amount is a sibling, not an ancestor, so nothing is returned from your .closest()
There is no single core js method for what you want to do. jQuery offers a function called .siblings() which is the closest to what you are intending to do, if you want to use it. But for pure javascript, the gist is to go up the dom/xpath tree and then back down.
There are different ways you can do it, depending on what selectors you want to be "anchors". #doodlemeister 's answer is one way to do it, but IMO it is not great because it may not be as flexible as you might like. It assumes going up to the first parent and then back down to the first child. IOW it expects your html structure to be exactly as you have it now.
A more flexible way is something like this:
const someList = document.querySelector('#someId ul');
someList.addEventListener('click', function(e) {
if (e.target.className === 'plus') {
let newValue = e.target.closest('li').querySelector('.amount');
newValue.innerText = +newValue.innerText + 5;
}
});
You can then adjust the .closest() selector if needed, and you use querySelector() to go back down the dom/xpath from there and target by your originally intended class.

.closest looks at ancestors, and .amount is not one. You can instead go to the .parentNode and then the .firstElementChild.
And I'd personally use .matches() instead of comparing the class name for exact equality.
someList.addEventListener('click', function(e) {
if (e.target.matches('.plus')) {
let newValue = e.target.parentNode.firstElementChild;
newValue.innerText += "5";
}
});

In javascript you can target the parent and select the matching child
const someList = document.querySelector('#someId ul');
someList.addEventListener('click', function(e) {
if (e.target.className === 'plus') {
let newValue = e.target.parentNode.querySelector('.amount').innerText += "5";
}
});
<div id="someId">
<h2>Test</h2>
<ul>
<li>
<p class="amount">5</p>
<span class="name">My Name</span>
<button class="plus">Plus</button>
</li>
<li>
<p class="amount">10</p>
<span class="name">My Name 2</span>
<button class="plus">Plus</button>
</li>
<li>
<p class="amount">15</p>
<span class="name">My Name 3</span>
<button class="plus">Plus</button>
</li>
</ul>
</div>

Related

Disable dropdown item with CSS or Javascript

I need to disable, deactivate or at least hide a dropdown item called Private request and I can only use CSS or Javascript to do so.
When I inspect the dropdown item it has the class a.js-dropdown-list. But every item in my dropdown has this class. So I can't just use {display: none;} because it will hide all options. Is there no more specific ID for every item in the drop down or can I deactivate items with Javascript?
Drop Down looks like this:
Here the code (1st block is for the picker field, 2nd for the drop-down menue):
<div id="js-organization-picker">
<sd-select class="js-share-with-organisation-picker is-private" data-type="link" data-id="customfield_10203" data-value="38" data-options="[{"label":"Private request","styleClass":"is-private","icon":"locked"},{"label":"Share with Testorganisation","value":38,"icon":"unlocked"}]" resolved="">
<a id="js-customfield_10203-dropdown-trigger" class="aui-dropdown2-trigger aui-button aui-button-link js-trigger customfield_10203-trigger select-dropdown-trigger aui-alignment-target aui-alignment-element-attached-top aui-alignment-element-attached-left aui-alignment-target-attached-bottom aui-alignment-target-attached-left active aui-dropdown2-active aui-alignment-enabled" aria-controls="customfield_10203-dropdown" aria-haspopup="true" role="button" tabindex="0" data-aui-trigger="" data-dropdown2-hide-location="js-customfield_10203-dropdown-container" resolved="" aria-expanded="true" href="#customfield_10203-dropdown">
<span class="aui-icon aui-icon-small aui-iconfont-locked">
: : before
</span> Private request
: : after
</a>
<input name="customfield_10203" type="hidden" class="js-input" value="">
<div id="js-customfield_10203-dropdown-container" class="hidden"></div>
</sd-select>
</div>
<div id="customfield_10203-dropdown" class="aui-dropdown2 filter-dropdown aui-style-default js-filter-dropdown select-dropdown aui-layer aui-alignment-element aui-alignment-side-bottom aui-alignment-snap-left aui-alignment-element-attached-top aui-alignment-element-attached-left aui-alignment-target-attached-bottom aui-alignment-target-attached-left aui-alignment-enabled" role="menu" aria-hidden="false" data-id="customfield_10203" resolved="" style="z-index: 3000; top: 0px; left: 0px; position: absolute; transform: translateX(602px) translateY(918px) translateZ(0px);" data-aui-alignment="bottom auto" data-aui-alignment-static="true">
<div role="application">
<ul class="aui-list">
<li>
<a class="js-dropdown-item " href="#">Private request</a>
</li>
<li></li>
<li>
<a class="js-dropdown-item " href="#" data-value="38">Share with Testorganisation</a>
</li>
<li></li>
</ul>
</div>
</div>
E.g. you could give the dropdown item ids to identify them. In HTML this would look like this: <p id="yourIdHere"></p>
You can access this item through Javascript using the document.getElementById() function like this: document.getElementById('yourIdHere').style.display = 'none';
If you can't edit the existing html code, youi have to get the element by it's name/value. This is a bit difficult. You have to iterate through all elements of that type and evaluate each name/value. If you have found the one, you was looking for, you can edit/hide it. You would do so (untested):
var elements = document.getElementsByTagName('div'); //div will be the name of the tag of your elements in the dropdown list
var length = elements.length;
for (var i=0, item; item = elements[i]; i++) {
if(item.value == "Private request") { //"Private request" is the name of the element we are looking for
item.style.display = 'none'; //Hide the element
}
}
You could loot trough all 'js-dropdown-items', check its innerText for 'Private request' and set its parentNodes display-property to 'none':
var list = document.getElementsByClassName('js-dropdown-item');
for(var i = 0; i < list.length; i++){
if(list[i].innerText === 'Private request') list[i].parentNode.style.display = 'none';
}
<ul class="aui-list">
<li>
<a class="js-dropdown-item " href="#">Private request</a>
</li>
<li></li>
<li>
<a class="js-dropdown-item " href="#" data-value="38">Share with Testorganisation</a>
</li>
<li></li>
</ul>
VannillaJS Solution document.querySelectorAll(".aui-list > li")[0].style.display = "none";
Welcome!
If I get you right there are plenty of elements with the same ID js-dropdown-list and you want to hide a specific one and there is no additional class for the element and you're not allowed to add specificity to it, let's say by adding of an additional class, you can do the following:
Grab all elements with this id by:
let elements = document.querySelectorAll('.js-dropdown-list'); // this is an array of these elements
let elementToHide = elements[n] // whene n is the number of that element
//hiding the element
elementToHide.style.display = 'none';
Hope that helps!
NOTE: I believe you will have to actually hide it OR use whatever you are using for this pseudo drop down (there was no reference in the question) to manage the disabled state if it provides that. Reference: https://www.w3.org/TR/2014/REC-html5-20141028/disabled-elements.html
Get the element by its text and then hide it. Might need the parent but this directly answers the question. Note this could all be wrapped in a function and then called from where you wish.
function HideByText(elId, findText) {
let group = document.getElementById(elId).getElementsByClassName('js-dropdown-item');
let found = Array.prototype.filter.call(group, function(el) {
return el.innerText === findText;
});
found.forEach(function(el) {
el.style.display = 'none';
});
return found;// in case you need the elements ref.
}
let foundFiltered = HideByText('customfield_10203-dropdown', 'Private request');
<div id="customfield_10203-dropdown" class="aui-dropdown2 filter-dropdown aui-style-default js-filter-dropdown select-dropdown aui-layer aui-alignment-element aui-alignment-side-bottom aui-alignment-snap-left aui-alignment-element-attached-top aui-alignment-element-attached-left aui-alignment-target-attached-bottom aui-alignment-target-attached-left aui-alignment-enabled"
role="menu" aria-hidden="false" data-id="customfield_10203" resolved="" data-aui-alignment="bottom auto" data-aui-alignment-static="true">
<div role="application">
<ul class="aui-list">
<li>
<a class="js-dropdown-item " href="#">Private request</a>
</li>
<li></li>
<li>
<a class="js-dropdown-item " href="#" data-value="38">Share with Testorganisation</a>
</li>
<li></li>
</ul>
</div>
</div>
Alternate for parent would be:
Change el.style.display = 'none'; to
if (node.parentElement) {
el.parentElement.style.display = 'none';
}
Have you tried using CSS? Not an ideal solution, but it might be better than using JS for this.
#js-organization-picker + .aui-dropdown2 .aui-list li:first-child {
display: none;
}
If you need to hide the first 2 elements, you could do something like:
#js-organization-picker + .aui-dropdown2 .aui-list li:first-child,
#js-organization-picker + .aui-dropdown2 .aui-list li:first-child + li {
display: none;
}

Javascript - get child of child

How can I set the innner html of a span element in dom that is under li>a>?
<li class="first">
<a>
<span aria-hidden="true">1</span>
</a>
</li>
I have tried something like this, but nothing was written:
element[i].childNodes[1].children.innerHTML = number;
children is an HTMLCollection. innerHTML only applies to HTML elements. You need to find the HTML element in the children before using innerHTML.
It doesn't make a lot of sense to mix the use of childNodes and children. Use the former for more compatibility across browsers, use the latter for a simpler approach that lets you consider only element nodes.
var li = document.querySelector("li");
var number = 13;
li.children[0].children[0].innerHTML = number;
<ul>
<li class="first">
<a>
<span aria-hidden="true">1</span>
</a>
</li>
</ul>
Alternatively, you could just use querySelector:
var li = document.querySelector("li");
var number = 13;
li.querySelector("a").innerHTML = number;
<ul>
<li class="first">
<a>
<span aria-hidden="true">1</span>
</a>
</li>
</ul>
I recommend using jQuery (http://jquery.com)
and then use can simply do
$('li > a > span').html('THE INNER HTML HERE');
or
$('li > a').find('span').html('THE INNER HTML HERE');
you can use .first() to the result of find() function to get the first element

Simple HTML Javascript button onclick function not working

So essentially I want to keep this as simple as possible, meaning no jquery or bootstrap etc... just straight javascript, HTML and CSS. This is what I have so far
Javscript:
var menuOptions= document.getElementsByClassName("nav");
var hamburger= document.getElementById("nav-btn");
function myFunction() {
hamburger.onclick= menuOptions.style.visibility= 'visible';
}
HTML:
<HTML>
<button onclick="myFunction()">
<span id="nav-btn">
<image src="Menugreen.png" alt="collapsable menu"/>
</span>
</button>
<div class="nav">
<ul>
<li id="Programs"> Programs </li>
<li> T-Shirts </li>
<li id="About"> About </li>
</ul>
</div>
</HTML>
CSS:
.nav {
visibility: hidden;
}
Besides just giving me a solution I would highly appreciate it if you could explain why my current method does not work and why yours does. Thanks in advance!
Two problems:
getElementsByClassName() returns a list, not a single element (though the list may contain just a single element), and that list doesn't have a .style property. You can use menuOptions[0] to access the first (and in this case only) element in the list.
You don't want to say hamburger.onclick= inside your function, because that would be assigning a new onclick handler but your function is already being called from the onclick attribute of your button. (Also, if you were trying to assign a new click handler you'd want hamburger.onclick = function() { /* something */ }.)
So the minimum change to your existing code to get it to work would be to change this line:
hamburger.onclick= menuOptions.style.visibility= 'visible';
...to this:
menuOptions[0].style.visibility = 'visible';
In context:
var menuOptions= document.getElementsByClassName("nav");
var hamburger= document.getElementById("nav-btn");
function myFunction() {
menuOptions[0].style.visibility = 'visible';
}
.nav {
visibility: hidden;
}
<HTML>
<button onclick="myFunction()">
<span id="nav-btn">
<image src="Menugreen.png" alt="collapsable menu"/>
</span>
</button>
<div class="nav">
<ul>
<li id="Programs"> Programs </li>
<li> T-Shirts </li>
<li id="About"> About </li>
</ul>
</div>
</HTML>
If you want repeated clicks on the button to toggle the menu display on and off then you can test the current visibility:
menuOptions[0].style.visibility =
menuOptions[0].style.visibility === 'visible' ? '' : 'visible';
Expand the following to see that working:
var menuOptions= document.getElementsByClassName("nav");
var hamburger= document.getElementById("nav-btn");
function myFunction() {
menuOptions[0].style.visibility =
menuOptions[0].style.visibility === 'visible' ? '' : 'visible';
}
.nav {
visibility: hidden;
}
<HTML>
<button onclick="myFunction()">
<span id="nav-btn">
<image src="Menugreen.png" alt="collapsable menu"/>
</span>
</button>
<div class="nav">
<ul>
<li id="Programs"> Programs </li>
<li> T-Shirts </li>
<li id="About"> About </li>
</ul>
</div>
</HTML>
There are a few reasons why your current setup does not function:
Document#getElementsByClassName returns a collection, and you are treating the result like a DOM element. You need to access an index like [0] to get an actual element.
Your toggle button only works one way, because visibility is set to visible but never set back to none when clicked again.
In myFunction, hamburger.onclick should not be assigned to the expression you chose. I am not sure why you tried to assign another click handler, but in order to make that work you would have needed to set it to a function () { ... }.
Now for my advice:
Use CSS classes to control whether the menu is hidden or not, rather than messing around with the style property in your JS. You can use the classList property of DOM elements to .add(), .remove(), and .toggle() a specific class when myFunction is run. I have chosen to use toggle because I think that most suits your use case.
Use element.addEventListener instead of HTML attributes like onclick.
Snippet:
var menuOptions = document.getElementsByClassName("nav")[0]
var hamburger = document.getElementById("nav-btn")
hamburger.parentNode.addEventListener('click', function myFunction() {
menuOptions.classList.toggle('hidden')
})
.nav.hidden {
visibility: hidden;
}
<button>
<span id="nav-btn">
<img src="Menugreen.png" alt="collapsable menu"/>
</span>
</button>
<div class="nav hidden">
<ul>
<li id="Programs"> Programs </li>
<li> T-Shirts </li>
<li id="About"> About </li>
</ul>
</div>

Append a child element to another div while having a reference to the orginal parent element

I have this html code
<div id="left-container" class="w3-container w3-left">
<div class="wordDiv w3-container w3-pale-green w3-leftbar w3-border-green w3-hover-border-red">
<h1>Give</h1>
<h3>Selected Definition:</h3>
<ul style="display:none;">
<li> offer in good faith </li>
<li> inflict as a punishment </li>
<li> afford access to </li>
</ul>
</div>
<div class="wordDiv w3-container w3-pale-green w3-leftbar w3-border-green w3-hover-border-red">
<h1>Up</h1>
<h3>Selected Definition:</h3>
<ul style="display:none;">
<li> to a higher intensity </li>
<li> to a later time </li>
<li> used up </li>
</ul>
</div>
</div>
<div id="right-container" class="w3-container w3-right"></div>
I want the user, once he click on one of the wordDiv's, to be able to see the potential definitions of that word in the right container (which are found in the "ul" element of each "wordDiv"), select one of the definitions, then I want to display the selected definition in the original wordDiv in the left-container.
You can found Jsfiddle Demo Here
A solution using jQuery. Updated Fiddle
$(function() {
$('.wordDiv').click(function() {
var txt = $(this).find('ul').html();
$('#right-container').html('<ul>' + txt + '</ul>')
})
})
Please check this fiddle
I have added li elements to another div based on the div which you are selected.
You can use the jQuery this variable in the click function, here is a working jQuery example of your request Updated Fiddle
It puts the li elements in the right div AND adds the onclick listener, which has the knowlege of its origin ;)
$('h1').click(function(){
var origin=$(this);
$(this).siblings('ul').children().click(function(){
$(this).parent().hide();
$(origin).parent().append(this);
$(this).replaceWith($('<div>' + this.innerHTML + '</div>'))
})
$(this).siblings('ul').show();
$("#right-container").append($(this).siblings('ul'));
})
$('ldi').click(function(){
$(this).parent().hide();
$(this).parent().parent().append(this);
$(this).replaceWith($('<div>' + this.innerHTML + '</div>'))
})

How to make selector that find class and data-id=X

<ul>
<li id="RadListBox1_i2" class="rlbItem ui-draggable">
<div class="ui-draggable ui-state-default" data-shortid="1007">
<em>ProductId: </em>
<span>110-01-070-10</span>
<br>
<em>ShortID: </em>
<span class="ShortID" data-shortid="1007">1007</span>
<br>
<em>Product Name: </em>
<span>Clearly Retro Style Colour Name Necklace</span>
<br>
<em>
</div>
</span>
</li>
<li id="RadListBox1_i3" class="rlbItem ui-draggable">
<li id="RadListBox1_i4" class="rlbItem ui-draggable">
</ul>
I need to build selector that find element that contains id=X and disable this item by .draggable('disable');
Some think like this:
find class y where data-shortid=X and make it
$("ul li").find(".ShortID").attr("data-shortid="+X+).draggable('disable');
Answer :
$("span.ShortID[data-shortid="+ShortId+"]").parents("li:first").draggable("disable");
You need to disable the li, not the span, so you need to find the parent:
$("span.ShortID[data-shortid=1007]").parents("li").draggable("disable");
$(".ShortID[data-shortid=1007]")
Above code will return you the element with class="ShortID" and attribute data-shortid="1007"When the element is picked, you can do anything you want with it.
Try this (jQuery Attribute Equals Selector):
$("ul li .ShortID[data-shortid='"+X+"']").draggable('disable');
Use this code:
$("ul li.ShortID[data-shortid="+X+"]").draggable('disable');
EDITED:
var obj = $("ul li span.ShortID");
var ids = obj.attr("data-shortid");
if(ids == X) {
obj.draggable('disable');
}
From JQuery documentation:
$( ",someclass[data-id='yourid']" ).dosomething();
$("ul li").find(".ShortID").attr("data-shortid="+X+).draggable('disable');
Would find all elements that have is inside a ul li that has a class .ShortID.
To grab a element by an attribute and value. You could do
$("div[data-shortid=" + x );
Beware that in your example you have to divs with the same ID.
http://api.jquery.com/attribute-equals-selector/
Btw. Keep using find to nest deeper as in your example, this will help on performence.

Categories