Target multiple elements from the style of element - javascript

Lets say i have multiple divs with the same class like
<div class="myDiv">1</div>
<div class="myDiv">2</div>
<div class="myDiv" style="display:none">3</div>
<div class="myDiv">4</div>
And here i want to target the div that has display:none which is the third div <div class="x" style="display:none">3</div>
I tried using this
const elements = document.querySelectorAll('.myDiv');
const element = elements.find(element => element.style.display == "none");
console.log(element)
but i got an error saying
elements.find is not a function
i tried to console.log the style of the elements to check the styles
const elements = document.querySelectorAll('.myDiv');
elements.forEach(element => console.log(element.style.display));
and it outputs this which is correct
""
""
"none"
""
But why doesn't the code that i tried work and why is elements.find not a function?

but i got an error saying
elements.find is not a function
That's because the NodeList returned by querySelectorAll isn't an array, it's a NodeList. You could create an array from it before doing the find:
const elements = document.querySelectorAll('.myDiv');
const element = [...elements].find(element => element.style.display == "none");
// −−−−−−−−−−−−−^^^^−−−−−−−−^
console.log(element);
That works because NodeList instances are iterable, so you can use spread notation to expand them into an array literal. (Other options are Array.from(elements), Array.prototype.slice.call(elements), or just using a for loop.)
You can also narrow your selector from .myDiv to .myDiv[style] so it only matches .myDiv elements that have a style attribute.
Ideally, though, if you can change things so that you don't have that inline style in the first place, I'd do that. For instance, if you use a class to hide the element:
<div class="myDiv">1</div>
<div class="myDiv">2</div>
<div class="myDiv hidden">3</div>
<div class="myDiv">4</div>
where hidden is defined as :
.hidden {
display: none;
}
then you can ask the browser for the specific element you're interested in:
const element = document.querySelector(".myDiv.hidden");

Another way is to use Array.from
like so :
const elements = document.querySelectorAll('.myDiv');
const element = Array.from(elements).find(element => element.style.display == "none");
console.log(element)

Try that it's will diplay none the div number 3, any way you can use id as well to display none
<div class="myDiv">1</div>
<div class="myDiv">2</div>
<div class="myDiv" style="display:none">3</div>
<div class="myDiv">4</div>
JS***
let myDiv = document.getElementsByClassName("myDiv");
myDiv[2].style.display = "none";

Related

Html selector returns an html collection and I don't know how to get to the element I need to make changes on

I have 2 divs: 1 on the left half of the page (A), one on the right (B). When hovering over a certain element of the right section, I want something to be displayed over the left one.
I did this using the following approach:
<div className="A">
<div className="hidden-div1">DIV 1</div>
<div className="hidden-div2">DIV 2</div>
<div className="hidden-div3">DIV 3</div>
</div>
<div className="B">
<div className="base-div1">
<h2 onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>Project 1</h2>
</div>
</div>
mouseOver(e){
const hiddenDiv1 = document.querySelector(".hidden-div1");
hiddenDiv1.style.display = "block";
}
mouseOut(e){
const hiddenDiv1 = document.querySelector(".hidden-div1");
hiddenDiv1.style.display = "none";
}
Problem is, considering I have 3 different hidden-divs and 3 different base-divs, I wanted to make 2 universal mouseOver and mouseOut functions for all of them. The way I tried it, is this:
mouseOver(e){
let hiddenDivName = "hidden-div" + e.target.className.slice(-1);
let hiddenDivSelector = document.getElementsByClassName(hiddenDivName);
hiddenDivSelector.style.display = "block";
}
but it returns "Cannot set property 'display' of undefined".
I tried console logging hiddenDivSelector and it shows an HTML collection and I don't know how to get my element. I've tried reading about it and visiting other questions but I couldn't apply anything to my situation
Event target returns a reference to DOM element. On DOM elements we can use getAttribute method and replace all non-digit characters by ''; result may be used to search DOM and iterate over returned array;
mouseOver(e){
let hiddenDivName = "hidden-div" + e.target.getAttribute('class').replace(/\D/g, '');
let hiddenDivSelector = document.getElementsByClassName(hiddenDivName);
Array.from( hiddenDivSelector ).forEach(el => el.style.display ) = "block";
}

How to re-Order html child elements in Dom based on id value

I have a parent div with some child elements. I want to re-order child elements based on two id values. for example 1,4. It means to grab the item with id 1 and insert it above the item with id 4.
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Making a drag and drop component for react. And this is what i have tried
const element = document.getElementById('1') //dragStart
const targetElement = document.getElementById('4') //dragEnter
const parent = document.querySelector('.parent') // drop
parent.insertBefore(element, targetElement)
But problem is when i grab the first element and want to put it on the bottom (last child). It fails to do so. How to put a child element after last child with insertBefore() method?
Don't know how you are using insertBefore() but there should not be any issues:
Update: The issue could be that your code is running before the DOM is fully loaded. You can wrap your code with DOMContentLoaded:
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const element = document.getElementById('1') //dragStart
const targetElement = document.getElementById('4') //dragEnter
const parent = document.querySelector('.parent') // drop
parent.insertBefore(element, targetElement)
});
</script>
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Placing the first element as the last element using nextSibling:
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const parentNode = document.querySelector('.parent');
const element = document.getElementById('1') //dragStart
const targetElement = document.querySelector('.parent').lastElementChild //get last child
parentNode.insertBefore(element, targetElement.nextSibling);
});
</script>
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Note: This answers the original question. The question has now been edited to reference React. You wouldn't use the following in a React project. You'd reorder the state that the DOM represents, and then let React handle updating the DOM.
You're right to use insertBefore:
function moveElement(move, before) {
// Get the element to move
const elToMove = document.getElementById(move);
// Get the element to put it in front of
const elBefore = document.getElementById(before);
// Move it
elBefore.parentNode.insertBefore(elToMove, elBefore);
}
function moveElement(move, before) {
const elToMove = document.getElementById(move);
const elBefore = document.getElementById(before);
elBefore.parentNode.insertBefore(elToMove, elBefore);
}
setTimeout(() => {
moveElement("1", "4");
}, 800);
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Side note: I suggest avoiding having id values that start with digits. Although they're perfectly valid HTML and they work just fine with getElementById, they're a pain if you need to target them with CSS, because a CSS ID selector (#example) can't start with an unescaped digit. For instance, document.querySelector("#1") fails. You have to escape the 1 with a hex sequence, which isn't terrifically clear: document.querySelector("#\\31") (the characters \, 3, and 1: 0x31 = 49 = the Unicode code point for 1).

JavaScript tag system: show only the divs that have all the classes listed in an array, else hide

Please explain how to do this!
I want to be able to hide/show a div based on its classes and an array.
If something is in an array, then it will only show divs which have the something class.
If nothing is in an array, every div will be shown.
If something and johnskeet are in the array, then it will only show divs which have both the something and johnskeet classes.
At a high level, one option would to have all of the divs hidden by default and then use document.querySelectorAll() to apply an 'unhidden' style to each div that is in the array.
Here is a question that should help with selecting divs with multiple classes that should help as well.
queryselectorAll - find an element with more than one matching class
I would recommend you to dynamically generate appropriate selector from the array, show all the matching elements and hide all other elements by default:
JavaScript
const classes = ['foo', 'bar', 'baz']
,selector = classes.map(x => '.' + x).join('')
$(selector).show()
CSS
div {
display: none;
}
See demo on JS Fiddle.
Alternatively, if you work on a high amount of elements, it would be better to generate appropriate style sheet in JavaScript:
const classes = ['foo', 'bar', 'baz']
,selector = classes.map(x => '.' + x).join('')
,$style = $(`<style>div {display: none;} div${selector} {display: block;}</style>`)
$('head').append($style)
To revert that, simply remove the <style> element by calling $style.remove().
See demo on JS Fiddle.
Assuming the elements are initially shown, you only need to hide the undesired ones. You can insert a stylesheet with
var rule = array.map(c => 'div:not(.' + CSS.escape(c) + ')').join()
+ '{display:none}';
sheet.insertRule(rule, 0);
var array = ["something", "johnskeet"];
var rule = array.map(c => 'div:not(.' + CSS.escape(c) + ')').join() + '{display:none}';
var style = document.createElement('style');
document.head.appendChild(style);
var sheet = style.sheet;
sheet.insertRule(rule, 0);
<div class="foo">foo</div>
<div class="something">something</div>
<div class="something johnskeet">something johnskeet</div>
<div class="something foo bar johnskeet">something johnskeet foo bar</div>
However, if some elements may already be hidden, you will need to set the desired display with higher specificity. The problem is that some elements are by default block-level, and some are inline-level. jQuery's toggle can be helpful to detect that:
$('div').each(function() {
$(this).toggle(array.every(c => this.classList.contains(c)));
});
var array = ["something", "johnskeet"];
$('div').each(function() {
$(this).toggle(array.every(c => this.classList.contains(c)));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="foo">foo</div>
<div class="something">something</div>
<div class="something johnskeet">something johnskeet</div>
<div class="something foo bar johnskeet">something johnskeet foo bar</div>
Another option would be display: revert, but it's not widely supported yet.
You may do as follows with pure JS. Run the code multiple times to see the results for randomly generated classArray.
var classes = ["something","johnskeet"],
classArray = classes.reduce((p,c) => Math.random() > 0.5 ? p.concat(c) : p,[]),
divs = document.querySelectorAll(".something, .johnskeet");
arrayStatus.textContent = "The current classes array is: " + JSON.stringify(classArray);
for (var div of divs) div.style.display = classArray.length === div.classList.length ? classArray.every(c => [...div.classList].includes(c)) ? "" : "none"
: !classArray.length ? "" : "none";
.something {background-color: paleGreen}
.johnskeet {background-color: tomato}
.something.johnskeet {background-color: thistle}
<p id="arrayStatus"></p>
<div class="something johnskeet">SJ</div>
<div class="johnskeet">J</div>
<div class="johnskeet">J</div>
<div class="something">S</div>
<div class="something">S</div>
<div class="johnskeet">J</div>
<div class="something johnskeet">SJ</div>
<div class="johnskeet">J</div>
<div class="something johnskeet">SJ</div>
No need to do anything fancy.
var array = ["something", "foo"];
Need to convert your array in such way that creates needed selector.
$('div').not("."+array.join(".")).hide();
Show element which has at-least once class from array.
$('div').not("."+array.join(",.")).hide();
If you've large amount of elements, you can use each() also.
var array = ["something", "johnskeet"];
var selector="";
$.each(array, function(key, value){
selector += "."+value;
});
console.log(selector);
$('div').not(selector).hide();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="foo">foo</div>
<div class="something">something</div>
<div class="something johnskeet">something johnskeet</div>
<div class="something foo bar johnskeet">something johnskeet foo bar</div>
<div class="dummy">dummy</div>

Get all items that start with class name

I'm trying to only show certain divs. The way I have decided to do this is to first hide all elements that start with "page" and then only show the correct divs. Here's my (simplified) code:
<form>
<input type="text" onfocus="showfields(1);">
<input type="text" onfocus="showfields(2);">
</form>
<div class="page1 row">Some content</div>
<div class="page1 row">Some content</div>
<div class="page2 row">Some content</div>
<div class="page2 row">Some content</div>
<script>
function showfields(page){
//hide all items that have a class starting with page*
var patt1 = /^page/;
var items = document.getElementsByClassName(patt1);
console.log(items);
for(var i = 0; i < items.length; i++){
items[i].style.display = "none";
}
//now show all items that have class 'page'+page
var item = document.getElementsByClassName('page' + page);
item.style.display = '';
}
</script>
When I console.log(items); I get a blank array. I'm pretty sure the regexp is right (get all items starting with 'page').
The code I'm using is old school JS, but I'm not adverse to using jQuery. Also if there is a solution that doesn't use regexp, that's fine too as I'm new to using regexp's.
getElementsByClassName only matches on classes, not bits of classes. You can't pass a regular expression to it (well, you can, but it will be type converted to a string, which is unhelpful).
The best approach is to use multiple classes…
<div class="page page1">
i.e. This div is a page, it is also a page1.
Then you can simply document.getElementsByClassName('page').
Failing that, you can look to querySelector and a substring matching attribute selector:
document.querySelectorAll("[class^=page]")
… but that will only work if pageSomething is the first listed class name in the class attribute.
document.querySelectorAll("[class*=page]")
… but that will match class attributes which mention "page" and not just those with classes which start with "page" (i.e. it will match class="not-page".
That said, you could use the last approach and then loop over .classList to confirm if the element should match.
var potentials = document.querySelectorAll("[class*=page]");
console.log(potentials.length);
elementLoop:
for (var i = 0; i < potentials.length; i++) {
var potential = potentials[i];
console.log(potential);
classLoop:
for (var j = 0; j < potential.classList.length; j++) {
if (potential.classList[j].match(/^page/)) {
console.log("yes");
potential.style.background = "green";
continue elementLoop;
}
}
console.log("no");
potential.style.background = "red";
}
<div class="page">Yes</div>
<div class="notpage">No</div>
<div class="some page">Yes</div>
<div class="pageXXX">Yes</div>
<div class="page1">Yes</div>
<div class="some">Unmatched entirely</div>
Previous answers contain parts of the correct one, but none really gives it.
To do this, you need to combine two selectors in a single query, using the comma , separator.
The first part would be [class^="page"], which will find all the elements whose class attribute begins with page, this selector is thus not viable for elements with multiple classes, but this can be fixed by [class*=" page"] which will find all the elements whose class attribute have somewhere the string " page" (note the space at the beginning).
By combining both selectors, we have our classStartsWith selector:
document.querySelectorAll('[class^="page"],[class*=" page"]')
.forEach(el => el.style.backgroundColor = "green");
<div class="page">Yes</div>
<div class="notpage">No</div>
<div class="some page">Yes</div>
<div class="pageXXX">Yes</div>
<div class="page1">Yes</div>
<div class="some">Unmatched entirely</div>
You can use jQuery solution..
var $divs = $('div[class^="page"]');
This will get all the divs which start with classname page
$(document).ready(function () {
$("[class^=page]").show();
$("[class^=page]").hide();
});
Use this to show hide div's with specific css class it will show/hide all div's with css class mention.

How to get all CSS classes of an element?

I have an element with multiple classes and I'd like to get its css classes in an array. How would I do this? Something like this:
var classList = $(this).allTheClasses();
No need to use jQuery for it:
var classList = this.className.split(' ')
If you for some reason want to do it from a jQuery object, those two solutions work, too:
var classList = $(this)[0].className.split(' ')
var classList = $(this).prop('className').split(' ')
Of course you could switch to overkill development mode and write a jQuery plugin for it:
$.fn.allTheClasses = function() {
return this[0].className.split(' ');
}
Then $(this).allTheClasses() would give you an array containing the class names.
Note that you can also use myElement.classList as a simple array-like object:
const classList = myElement.classList;
This is supported by all major browsers for a while now, apart IE 9 and below.
This should do the work for you:
var classes = $('div').attr('class').split(" ");
This would be the jQuery solution for other solutions there are other answers !
Check this out:
var classes = $('selector').prop('classList');
element.classList.value
console.log("class")
console.log(document.getElementById('c2').classList.value)
<div id="c2" class="class1 class2"> i am two class</div>
getAttribute
console.log("class")
console.log(document.getElementById('c2').getAttribute('class'))
<div id="c2" class="class1 class2"> i am two class</div>
className
console.log("class")
console.log(document.getElementById('c2').className)
<div id="c2" class="class1 class2"> i am two class</div>
to make an array choose any one of above method
string.split(' ');
function showClasses() {
const div = document.querySelector('div');
const classes = div.className.split(' ');
const p = document.querySelector('p');
p.innerHTML = classes;
}
<div class="foo bar">This div has foo, bar classes</div>
<p class='output'>Above div classes appear here</p>
<button onClick="showClasses();">Show div classes</button>
HTML
<div class="foo bar">This div has foo, bar classes</div>
Vanilla JavaScript. It will return an array of classes.
const div = document.querySelector('div');
const classes = div.className.split(" "); // ['foo', 'bar']

Categories