querySelectorAll.style does not work - javascript

I am writing something in JavaScript that I need to use querySelectorAll.style but it always returns undefined, but it works perfectly with querySelector.style. How can I make it work properly so I can set the style?
document.querySelector("div#tabs" + tabId + "> div.page").style.display = 'none'; //works
document.querySelectorAll("div#tabs" + tabId + "> div.page").style.display = 'none';// doesn't work

querySelector:
Returns the first element within the document...
querySelectorAll:
Returns a list of the elements within the document...
IE in the first one, you're operating on a single element, which does have a style property. The second one is a list of elements, so you need to loop over that list applying the style:
var els = document.querySelectorAll("div#tabs" + tabId + "> div.page");
for (var x = 0; x < els.length; x++)
els[x].style.display = 'none';

querySelectorAll returns a list of elements rather than a single one.
So this should work to apply the style to the first element found:
document.querySelectorAll("div#tabs" + tabId + "> div.page")[0].style.display = 'none'; // First element

querySelectorAll returns a html collection of elements, not a single element, so you need to loop over the results:
Array.from(document.querySelectorAll("div#tabs" + tabId + "> div.page"))
.forEach(function(val) {
val.style.display = 'none';
});

Related

Pure JS: Store list of data- attributes in array, then iterate

I'm fairly new to JS and I can do DOM manipulation and if/else statements by hand. Now I'm trying for something out of my league, combining iteration with arrays, and I have a bit of a hard time understanding both of them.
With this in mind: Considering this div: <div id="firstAnchor"> would act as an anchor to this link:
I want to store the ID of these div's (id's should be able to be anything):
<div id="firstAnchor" style="display: inline-block;">First title</div>
<div id="secondAnchor" style="display: inline-block;">Second title</div>
<div id="thirdAnchor" style="display: inline-block;">Third title</div>
into an array, and then create these three links automatically* placed in a div called "anchorLinks":
Link to first title
Link to second title
Link to third title
How would I go about this?
*
for example within this function:
(function create_anchor_link_list() {
//placed here
})();
Edit:
Here is what I have tried to begin with. I first had data-anchor="firstAnchor" etc. on my div elements until I realized I couldn't link to div elements based on data- attributes values. So with the data- attributes I tried:
(function anchorsInPage2(attrib) {
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# anchorsInPage2 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log(" ");
var elements = document.getElementsByTagName("*");
var foundelements = [];
for (var i = 0; i < elements.length; i++) {
if (elements[i].attributes.length > 0) {
for (var x = 0; x < elements[i].attributes.length; x++) {
if (elements[i].attributes[x].name === attrib) {
foundelements.push(elements[i]);
}
}
}
}
return foundelements;
console.log(" ");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# / anchorsInPage2 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
})();
function anchorsInPage3() {
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# anchorsInPage3 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log(" ");
var elements = document.getElementsByTagName("*");
var foundelements = [];
for (var i = 0; i < elements.length; i++) {
if (elements[i].attributes.length > 0) {
for (var x = 0; x < elements[i].attributes.length; x++) {
if (elements[i].attributes[x].name === "anchor") {
foundelements.push(elements[i]);
}
}
}
}
return foundelements;
console.log(" ");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# / anchorsInPage3 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
}
(function anchorsInPage1() {
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# anchorsInPage1 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log(" ");
var anchors = document.querySelectorAll('[anchor]');
for(var i in anchors){
console.log(i);
}
console.log(" ");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# / anchorsInPage1 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
})();
First update after further testing:
Barmar's example was used. The text below is a direct answer to Barmar (too long for the other comment field)
Test: http://jsfiddle.net/e5u03g4p/5/
My reply:
With the first variable you found all element with the attribute data-anchor, so I guess the brackets in querySelectorAll tells it which specific attribute we mean instead of what elements ID's we want, which is the "standard" writing document.querySelectorAll("tagName") instead of document.querySelectorAll("[attributeName]").
With the second variable you found the first element with the ID of anchorLinks. The hashtag is needed to specify ID as querySelector represents div so the result is div#anchorLinks(?).
You then take the variable anchors (which results in an array of the data-anchor value of the div's with the data-anchor attribute) and for each of them, a function triggers where the d argument of the function equals the element ID of the elements with the data-anchor attribute. Everything within this function repeats for each of the elements with data-anchor attribute (ie. the variable anchors).
What's happening within the function is:
-You create a variable (a) which contains the element creation of an <a> element
-You then set the href attribute of the newly created <a> element to the ID
of the data-anchor elements.
-I then assign the attribute title of the <a> elements to the content of the data-anchor elements (instead of the original thought where it was textContent that was set to the <a> elements`as I want the links to be images instead of text)
-I then also added a new class attribute to the <a> elements in order to style them
If you used data-anchor="something" in your DIVs, then you should use
var anchors = document.querySelectorAll('[data-anchor]');
not [anchor].
You can then loop over them with forEach()
var anchorLinks = document.querySelector("#anchorLinks");
anchors.forEach(function(d) {
var a = document.createElement('a');
a.href = '#' + d.id;
a.textContent = 'Link to ' + d.textContent.toLowerCase();
anchorLinks.appendChild(a);
});
If you craft the query selector correctly you can get all of the "anchor" elements at once, then iterate over them to add the relevant links.
var links = document.getElementById('anchorLinks');
document.querySelectorAll('#anchors div[id]').forEach(function(anchor) {
var link = document.createElement('a');
link.href = '#' + anchor.id;
link.textContent = 'Link for ' + anchor.textContent;
links.appendChild(link);
});
<div id="anchors">
<div id="firstAnchor" style="display: inline-block;">First title</div>
<div id="secondAnchor" style="display: inline-block;">Second title</div>
<div id="thirdAnchor" style="display: inline-block;">Third title</div>
</div>
<div id="anchorLinks">
</div>
How about this:
document.getElementsByTagName('div').forEach(function(d) {
var a = document.createElement('a');
a.setAttribute('href', '#' + d.id);
a.innerHTML = 'Link to ' + d.textContent.toLowerCase();
document.getElementById('anchorLinks').appendChild(a);
});
Or if you have more divs (of course) and they have a specific class, you can do:
document.getElementsByClassName('your-class-name').forEach(function(d) {
var a = document.createElement('a');
a.setAttribute('href', '#' + d.id);
a.innerHTML = 'Link to ' + d.textContent.toLowerCase();
document.getElementById('anchorLinks').appendChild(a);
});

Cannot find element based on attribute

I am trying to raise a mouseenter event for an element, when hovering over another element. I am trying to find the element based on a data attribute:
$("#modal").on("mouseenter", ".jq_pin", function(e) {
var attr = $(this).data("abbr");
var els = $(".jq_mapPoint");
var el = els.find("[data-abbr='" + attr + "']");
el.trigger(e.type);
});
My event fires, and debugging I can see a list of elements with jq_mapPoint class, and one has a matching data attribute, but the length of el is always 0.
Element with event:
<li class="jq_pin"data-abbr="N">HI</li>
Element Im trying to target:
<div style="position:absolute;left:59%;bottom:72%" class="jq_mapPoint" data-abbr="N">N</div>
You need .filter() instead of .find()
Reduce the set of matched elements to those that match the selector or pass the function's test.
var el = els.filter("[data-abbr='" + attr + "']");
instead of
var el = els.find("[data-abbr='" + attr + "']");
It probably needs the All selector *:
theels.find("*[data-abbr='" + attr + "']");

Removing elements from a document in Javascript [duplicate]

This question already has answers here:
JavaScript DOM remove element
(4 answers)
Closed 8 years ago.
Working on building this to-do list app to learn JS better.
I am able to insert text into box and have it add a new div element to the page with the relevant text residing within the div. The code that adds the div to the page automatically applies the class .newItem then adds an incrementing id i0, i1, i2, etc each time it's clicked. Everything works without an issue. Now, I have been fiddling around with the code to be able to click a single div element and have it remove itself from the page, but can't seem to get it to work.
var iDN = 0;
//Function that adds a new div with a custom incrementing id number
document.getElementById('add_button').onclick = function () {
var taskName = document.getElementById('add_task').value; // Store the value in the textbox into a variable
document.querySelector('.shit_to_do').innerHTML += '<div class = "newItem" id = "i' + iDN + '"' + 'onclick = removeEl()' + '>' + taskName + '</div>';
iDN += 1;
};
document.getElementById('testing').onclick = function () {
var parentNode = document.querySelector('.shit_to_do');
parentNode.removeChild(parentNode.children[0]);
}
function removeEl() {
for (i = 0; i < iDN; i++) {
if (document.getElementById('i' + i).onclick) {
document.getElementById('i' + i).display.style = 'none';
}
alert(i);
}
}
The for loop was really some random option I was trying to figure out how things were working onclick for each div, but didn't get to far with that one.
tl;dr:
I want to add click events to the new divs added onto the page in a single, universal function.
The value of document.getElementById('i' + i).onclick will be null if you've not set a handler to this attribute/property, otherwise it will be a function. null is always falsy, a function is always truthy.
To remove your element, you'll either have to look at this or e.target where e is the click event, and then call the DOM method node.removeChild(child).
The "quick and dirty" solution is to pass this into removeEl and remove it that way,
// ...
document.querySelector('.shit_to_do').innerHTML += '<div class="newItem" id="i' + iDN + '" onclick="removeEl(this)">' + taskName + '</div>';
// ...
function removeEl(elm) {
elm.parentNode.removeChild(elm);
}
I also removed the strange spacing between attribute names and values in your HTML
A perhaps "cleaner" solution is to create your nodes and attach listeners all by using DOM methods
function createDiv(index, taskname) {
var d = document.createElement('div');
d.setAttribute('id', 'i' + index);
d.textContent = taskname;
return d;
}
function removeElm() {
this.parentNode.removeChild(this);
}
var iDN = 0;
document.getElementById('add_button').addEventListener('click', function () {
var taskName = document.getElementById('add_task').value,
list = querySelector('.shit_to_do'),
div = createDiv(iDN, taskName);
div.addEventListener('click', removeElm);
list.appendChild(div);
iDN += 1;
});
This way means the browser does not re-parse any HTML as it not use element.innerHTML, which is a dangerous property may destroy references etc (along with usually being a bit slower)
Helpful links
node.addEventListener
document.createElement
node.appendChild

nextAll() returns undefined - jQuery

I am making organizatoinal-chart-like structure. All divs have unique ID and a class that matches the ID of their parent (if they have one) like so:
<div id="title1" class="container"></div>
<div id="title4" class="container child title3"></div>
<div id="title3" class="container child title2"></div>
<div id="title2" class="container child title1"></div>
Idea is to order these in chart by absolutely positioning divs below their parent div, so they will be rendered in the right order.
Problem with the code below is that the $test_str returns as undefined, even if I pass .child as selector... and I am stuck.
$containers = $('.container');
$test = [];
for ( i = 0; i < $containers.length; i++ ) {
$test_str = $($containers[i]).nextAll('.' + $containers[i].id);
$test.push($test_str);
console.log($containers[i].id + ' is parent ' + $test[i].id);
//$test[i].id is returned as undefined;
}
Here is the fiddle
I would take an approach of just moving the divs using appendTo or insertAfter instead of trying to absolutely position them (which I think would be more difficult). This example uses appendTo since it creates an actual parent-child structure instead simply re-ordering.
$.each($('.container'), function(ind, val) {
$.each($('.' + val.id), function(i,v) {
console.log(val.id + ' is parent ' + v.id);
$(v).appendTo(val);
});
});
JSFiddle
If you output the $test_str to the console, the element info is under context, so you'll want to get the id from $test[i].context.id
$containers = $('.container');
$test = [];
for ( i = 0; i < $containers.length; i++ ) {
jQuery.fn.init();
$test_str = $($containers[i]).nextAll('.' + $containers[i].id);
$test.push($test_str);
console.log($containers[i].id + ' is parent ' + $test[i].context.id);
}
The problem you have is that nextAll() returns a sequence of items and you are treating it as a single item. The second problem you are probably going to run into is you are using nextAll() instead of siblings().
siblings
Description: Get the siblings of each element in the set of matched elements, optionally filtered by a selector.
nextAll() returns only successors at the same level and not true siblings.
Description: Get all following siblings of each element in the set of matched elements, optionally filtered by a selector.
Here is the code showing a way to get the result you want. I took out the intermediate array for the time being as it would just further complicate this.
$containers = $('.container');
$test = [];
for ( i = 0; i < $containers.length; i++ ) {
$test_str = $($containers[i]).siblings('.' + $containers[i].id);
console.log($test_str);
for(j = 0; j < $test_str.length; j++){
console.log($containers[i].id + ' is parent ' + $test_str[j].id);
}
}
Remember when dealing with JQuery that most of the time the functions will return a sequence of items instead of a single item, even if there is only one match.

js - swap image on click

I'm trying to replace one div with another and turn the others off:
JS
function imgToSWF1() {
document.getElementById('movie-con2 movie-con3 movie-con4').style.display = 'none';
document.getElementById('movie-con').style.display = 'block';
}
function imgToSWF2() {
document.getElementById('movie-con movie-con3 movie-con4').style.display = 'none';
document.getElementById('movie-con2').style.display = 'block';
}
function imgToSWF3() {
document.getElementById('movie-con movie-con2 movie-con4').style.display = 'none';
document.getElementById('movie-con3').style.display = 'block';
}
function imgToSWF4() {
document.getElementById('movie-con movie-con2 movie-con3').style.display = 'none';
document.getElementById('movie-con4').style.display = 'block';
}
HTML
<span onmouseover="src=imgToSWF1();"><div class="numbers">01</div></span>
<span onmouseover="src=imgToSWF2();"><div class="numbers">02</div></span>
<span onmouseover="src=imgToSWF3();"><div class="numbers">03</div></span>
<span onmouseover="src=imgToSWF4();"><div class="numbers">04</div></span>
I can't seem to get this to work and I believe that targetting multiple ID's isn't possible like this? Anyway any advice would be smashing - thanks!
You're correct that you cannot supply multiple ids to document.getElementById(). Instead, you can grab them all individually using an array. There are many ways to accomplish what you are trying to do. This is a straightforward method based on iterating through the array of the elements to hide and hiding all of them.
This method expects you to define the array of nodes to hide in each of your functions, and so is non-ideal.
// Example:
function imgToSWF1() {
var nodes = ['movie-con2', 'movie-con3', 'movie-con4'];
// Loop over and hide every node in the array
for (var i=0; i<nodes.length; i++) {
document.getElementById(nodes[i]).style.display = 'none';
}
document.getElementById('movie-con').style.display = 'block';
}
Better:
This can be refactored into one function, however. Make one function which recieves the node you want to show as an argument. Hide the others. The array should contain all nodes that may be hidden in any circumstance, and be defined at a higher scope than the function.
// Array holds all nodes ids
var nodes = ['movie-con', 'movie-con2', 'movie-con3', 'movie-con4'];
// Function receives a node id which will be shown
function imageToSWF(nodeId) {
// Loop over and hide every node in the array
for (var i=0; i<nodes.length; i++) {
document.getElementById(nodes[i]).style.display = 'none';
}
// And unhide/show the one you want, passed as an argument
document.getElementById(nodeId).style.display = 'block';
}
Call as:
<span onmouseover="imageToSWF('movie-con');"><div class="numbers">01</div></span>
<span onmouseover="imageToSWF('movie-con2');"><div class="numbers">02</div></span>
<span onmouseover="imageToSWF('movie-con3');"><div class="numbers">03</div></span>
<span onmouseover="imageToSWF('movie-con4');"><div class="numbers">04</div></span>
You have to target one element at a time with document.getElementById, eg. document.getElementById('movie-con2').style.display='none'; document.getElementById('movie-con3').style.display='none';
etc, etc.
You could also use Jquery siblings selector to show or hide all elements that are siblings within a parent tag.
You definitely can't do that in straight up javascript. The document.getElementById function only returns a single element from the DOM.
Some docs can be had here:
http://www.w3schools.com/jsref/met_doc_getelementbyid.asp
https://developer.mozilla.org/en-US/docs/DOM/document.getElementById
If you were to use a toolkit like jQuery you could do something like:
$('div[id^="movie-con"]').hide(); // hide everything
$('div["movie-con"' + index + "]").show(); // show the one we want
Since you're not using jQuery it's not quite as easy. Something like:
var matches = document.getElementById("containerDiv").getElementsByTagName("img");
for( var i = 0; i < matches.length; i++ )
matches[i].style.display = "none";
document.getElementById('movie-con' + index).style.display = "block";

Categories

Resources