Finding if there any element in the set has style.display !== "none"; - javascript

var isAnyBool = $(selector)
.filter(function(){ return this.style.display !== "none"; })
.length;
// if (isAnyBool) { .. }
This works as expected but needlessly counts all elements when all it's needed is simple true/false. How it could be improved to be more efficient?
UPDATE: since visible doesn't know if element is directly hidden, or some of the parents are actually hidden, I have to check style.display or something equal in functionality

I don't know about performance efficiency, but this is far easier to read:
var isAnyBool = $(selector).is(':visible');
Edit #2:
I think #dandavis's answer is the best one

var isAnyBool = $(selector+"[style*='display: none']").length > 0 ;
should be MUCH faster in modern browsers than iteration of any type.
since jQuery's :visible can be affected by more than just style.display, it's not the right tool for this job.
this checks the style attrib only, and does so without js iteration, making it the fastest and simplest solution offered at time of writing.

You can make it simple with :visible
var isAnyBool = $(selector).filter(':visible').length;

Instead of filtering and counting the elements, you can use the .is() method with the same callback:
var isAnyBool = $(selector).is(function(){ return this.style.display !== "none"; })

Can't say for sure if this is more or less overhead, but seems more legible to me if you did it something like this:
$(selectSomeStuff).filter(!$(this).is(':visible'));
... or testable variable
var isAnyBool = $(selectSomeStuff).filter(!$(this).is(':visible')).length
... maybe as if statement
if ($(selectSomeStuff).filter(!$(this).is(':visible'))){
var isAnyBool = true;
// OR
//.. do stuff here ...
}

It doesn't count.. length is a property not a function. So there is not much going on when you call .length
Instead of storing the length, I would store the result in case if it can be used elsewhere.
var mySelector = $(selector).filter(function(){ return this.style.display !== "none"; })
if (mySelector.length) { .. }

Related

Hiding DIV with onclick functions - looking for a cleaner way to achieve the same result

First, I'd like to apologize if this is easily searchable; I'm fairly new to JavaScript and I don't know exactly what to search (hence why I'm asking here!)
Basically, I have some code that shows/hides divs based on what nav button the user clicks, eliminating the need for multiple pages of HTML. I have a working solution in JS, but my problem is that it seems way too blocky and needlessly complicated.
I know they'res a more elegant solution out there somewhere. However everything I looked at only deals with hiding/showing divs when you press one button, not multiple, as would be the case with a nav bar.
So, if you could point out a few different avenues to research, I'd appreciate it!
JS:
function hideContent1(){
var a = document.getElementById("zero");
var b = document.getElementById("one");
var c = document.getElementById("two");
var d = document.getElementById("threeA");
var e = document.getElementById("threeB");
if(a.style.display = "none"){
document.getElementById('zero').style.display = "block";
}
if(b.style.display = "none"){
document.getElementById('one').style.display = "block";
}
if(c.style.display = "block"){
document.getElementById('two').style.display = "none";
}
if(d.style.display = "block"){
document.getElementById('threeA').style.display = "none";
}
if(e.style.display = "block"){
document.getElementById('threeB').style.display = "none";
}
}
This repeats three times, with slightly different IF statements, for the three different nav buttons that can be pressed.
Like I said before, this code works fine, it's just needlessly bulky. What would be a better way to complete this task?
Assuming that the assignments inside the ifs were just typos, and assuming that the styles toggle between none and block, you may simply assign the block or none style regardless - if it doesn't change the existing style, nothing will happen.
You might consider using classes instead, to make the code less repetitive - eg, give a and b a class of content2, and give the rest a class of content1. Then, on hideContent1(), hide every content1, while showing all content2:
function hideContent1(){
document.querySelectorAll('.content2')
.forEach(elm => elm.style.display = 'block');
document.querySelectorAll('.content1')
.forEach(elm => elm.style.display = 'none');
}
Note that NodeList.prototype.forEach is a somewhat new method, so if you want to support older browsers, either use a polyfill, or convert the NodeList to an array before calling forEach.
Use a combined id selector, then get all elements using querySelectorAll and finally iterate and do the same. Although the if statements are not necessary since you are just togging them in case it's already visible and setting block doesn't make any change. In your current code, it will be always true since it's an assignment(=) and value is a non-empty string which would be truthy.
For getting elements based on CSS selector you can use querySelectorAll method and for iterating over the returned NodeList collection use NodeList#forEach method(polyfill for older browser).
function hideContent1() {
// get elements to show
var show = document.querySelectorAll('#zero,#one')
// get elements to hide
var hide = document.querySelectorAll('#two,#threeA,#threeB');
// iterate and update
show.forEach(ele => ele.style.display = 'block')
hide.forEach(ele => ele.style.display = 'none')
}
FYI : For grouping multiple elements you can use a common class which is always better than combining multiple selectors.
How about making it as a function?
for example:
function switchDisplay(source, target) {
if (source.style.display === 'none') {
target.style.display = 'block'
}
if (source.style.display === 'block') {
target.style.display = 'none'
}
}
Assuming the functionality is the one you really want to have & besides the typo as mentioned in the comments.

Any chance to reduce the size of this snippet of code?

IS there any way I could reduce the size of the snippet of code below? Something like if (!$('body#pagina_blog_1 to body#pagina_blog_10).length) Online javascript minifier tools do not help.
jQuery(function($){
if (!$('body#pagina_blog_1, body#pagina_blog_2, body#pagina_blog_3, body#pagina_blog_4, body#pagina_blog_5, body#pagina_blog_6, body#pagina_blog_7, body#pagina_blog_8, body#pagina_blog_9, body#pagina_blog_10').length)
return;
// do stuff
});
Yes you can: $('*[id^="pagina_blog_"]')
For more details refer jquery selectors: http://api.jquery.com/attribute-starts-with-selector/
If you know it's an id on the body tag, you don't even need to use selectors as you can just get the id string directly and compare it to anything you want using a regex. For example, you could do this:
if (document.body.id.match(/^pagina_blog_\d+$/)) {
// code here
}
Or, for just any one or two digits at the end:
if (document.body.id.match(/^pagina_blog_\d{1,2}$/)) {
// code here
}
Or, if you wanted to actually see if the number after the id is in some specific numeric range such as 1-10, you could do this:
var num, matches = document.body.id.match(/^pagina_blog_(\d+)$/);
if (matches) {
num = +matches[1];
if (num >= 1 && num <= 10) {
// code here
}
}
It's not really minifying, but how about just
if ( $('[id^="pagina_blog_"]').length === 0 ) {
// do stuff
}
Give them a shared class to select on.
If you must use the ids for some reason, I would suggest...
!$('body').is('[id^="pagina_blog_"]')
The reason you do not want to put the id selector as the first selector is this would result in a complete dom scan, which is not desired. However in your logic it looks like your only concerned with the body tag having it.

jQuery how does one peek ahead in each()?

I can't seem to find a definite answer for this. Consider the following:
var dupe = false;
$(".syndUID").sort(function(a,b) {
return a - b;
}).each(function(i, el) {
alert($(this).val() + " - " + $(this).next().val());
if($(this).val() == $(this).next().val()) {
dupe = true;
return;
};
});
This code is an attempt to find duplicate values in a set of inputs with the class syndUID. They are scattered about a form, so not next to eachother in the DOM.
next().val() is always undefined though. am I using the wrong function? How do I simply peek ahead to the next element? I have access to the index, but I don't know how to even make use of it.
EDIT:
After reading the comments and answers I realized there really is no proper iterator in jQuery, which seems really stupid to me since it provides each(). I also had another bug with the above code. Here is the final solution I used that works:
// duplicate check
var dupe = false;
var prevVal = undefined;
$(".syndUID").sort(function(a,b) {
return $(a).val() - $(b).val();
}).each(function() {
if($(this).val() == prevVal) {
dupe = true;
return false;
}
prevVal = $(this).val();
});
For anyone who finds this via google, the answers provided by others may be a decent alternative solution, but for my needs I found this sufficed.
You can do something like $('.syndUID')[i+1] to regrab the list, focusing on that element (and optionally turning it back into a jQuery object)
Using [i+1] will return a DOM element, but you can use .eq(i+1) to return a jQuery object. Or if you hate your future developers, wrap the DOM element in $() because screw readability!
As Andreas stated -- regrabbing the list is wasting memory. Before the loop, cache the object into a variable with var element = $('.syndUID') so you're not iterating the DOM so much.
next() grabs the next sibling - not the next ancestor or parent.
For example, if we have an unordered list, next() works like this:
var a = $('#listone.first').next().val();
console.log(a) //outputs "Second"
<ul id="listone">
<li class="first">First</li>
<li class="second">Second</li>
</ul>
<ul id="listtwo">
<li class="third">Third</li>
<li class="fourth">Forth</li>
</ul>
So, if you are trying to grab the next parent, .val() applied to .next() wont work. I dont know what your outputting to (table, UL, DIV, etc) so it is hard to give you a working solution - but that is likely your problem. You are not traversing the DOM properly.
Well, you have your sorted array already, maybe instead of trying to do this with .each() you just use a simple for loop?
var elemArray = $(".syndUID").sort(function(a,b) {
return a - b;
});
for(var i = 0; i < elemArray.length; i++){
if(elemeArray[i].value == elemArray[i+1].value){
//do something with comparison
}
}
However, this will only check for a duplicate in the next syndUID element in the array. In order to do a complete search you would need to check each element against every element (excluding itself) from the array. That would involve a nested loop which will add an order of n^2 to your function

JavaScript & string length: why is this simple function slow as hell?

i'm implementing a charcounter in the UI, so a user can see how many characters are left for input.
To count, i use this simple function:
function typerCount(source, layerID)
{
outPanel = GetElementByID(layerID);
outPanel.innerHTML = source.value.length.toString();
}
source contains the field which values we want to meassure
layerID contains the element ID of the object we want to put the result in (a span or div)
outPanel is just a temporary var
If i activate this function, while typing the machine really slows down and i can see that FF is using one core at 100%. you can't write fluently because it hangs after each block of few letters.
The problem, it seems, may be the value.length() function call in the second line?
Regards
I can't tell you why it's that slow, there's just not enough code in your example to determine that. If you want to count characters in a textarea and limit input to n characters, check this jsfiddle. It's fast enough to type without obstruction.
It could be having problems with outPanel. Every time you call that function, it will look up that DOM node. If you are targeting the same DOM node, that's very expensive for the browser if it's doing that every single time you type a character.
Also, this is too verbose:
source.value.length.toString();
This is sufficient:
source.value.length;
JavaScript is dynamic. It doesn't need the conversion to a string.
I doubt your problem is with the use of innerHTML or getElementById().
I would try to isolate the problem by removing parts of the function and seeing how the cpu is used. For instance, try it all these ways:
var len;
function typerCount(source, layerID)
{
len = source.value.length;
}
function typerCount(source, layerID)
{
len = source.value.length.toString();
}
function typerCount(source, layerID)
{
outPanel = GetElementByID(layerID);
outPanel.innerHTML = "test";
}
As artyom.stv mentioned in the comments, cache the result of your GetElementByID call. Also, as a side note, what is GetElementByID doing? Is it doing anything else other than calling document.getElementById?
How would you cache this you say?
var outPanelsById = {};
function getOutPanelById(id) {
var panel = outPanelsById[id];
if (!panel) {
panel = document.getElementById(id);
outPanelsById[id] = panel;
}
return panel;
};
function typerCount(source, layerId) {
var panel = getOutPanelById(layerId);
panel.innerHTML = source.value.length.toString();
};
I'm thinking there has to be something else going on though, as even getElementById calls are extremely fast in FF.
Also, what is "source"? Is it a DOMElement? Or is it something else?

Test if two elements are the same

I would suspect this to work at first:
if ($('#element') == $('#element')) alert('hello');
But it does not. How does one test if elements are the same?
As of jquery 1.6 you can now simply do:
$element1.is($element2)
This should work:
if ($(this)[0] === $(this)[0]) alert('hello');
so should this
if (openActivity[0] == $(this)[0]) alert('hello');
Or just
if (openActivity[0] == this) alert('hello');
(without a new jQuery instance ;-)
As somebody already told, the same HTML element wrapped in two different moments generates two different jQuery instances, so they can never be equal.
Instead, the HTML elements wrapped may be compared that way, since the memory location they occupy is the same if it is the same HTML element, so:
var LIs = $('#myUL LI');
var $match = $('#myUL').find('LI:first');
alert(LIs.eq(0) === $match); // false
alert(LIs.get(0) === $match.get(0)) // TRUE! yeah :)
Best regards!
I would use addClass() for marking the opened and you can check that easily.
9 years later, without jQuery
If two elements are the same one, two elements must have the same pointer.
Thus,
document.body === document.body // true
document.querySelector('div') === document.querySelector('div') // true
document.createElement('div') === document.createElement('div') // false
Like silky or Santi said, a unique ID or class would be the easiest way to test. The reason your if statements don't work like you'd expect is because it's comparing 2 objects and seeing if they're the same object in memory.
Since it's always a new object getting created by $(this), they can never equal each other. That's why you have to test on a property of the object. You could get away with no unique id/class if each openActivity element was guaranteed to have different content that you could test against.

Categories

Resources