Js - Append a value to element CSS - javascript

This seems so simply yet its not working (undefined).
I have set a var to <ul> which is a child of <div> element "feature_tabs_indicators".
The pBoxShadowProperty function gets the BoxShadow property supported by the current browser.
And the final statement merely sets the pBoxShadowProperty to 0, i.e. its overriding the CSS set Box-Shadow property.
Can someone please explain what I am doing wrong here in the last statement?
Best,
var iActiveNo = 0;
var eTabInd = document.getElementById ("feature_tabs_indicators").children[0];
var pBoxShadowProperty = getSupportedCSSproperty(["boxShadow", "mozBoxShadow", "webkitBoxShadow"]);
function getSupportedCSSproperty (propertyArray)
{
var root = document.documentElement;
for (var i = 0; i < propertyArray.length; i++)
{
if (typeof root.style[propertyArray[i]] === "string")
{
return propertyArray[i];
}
}
}
iActiveNo = iActiveNo + 1;
eTabInd.children[iActiveNo - 1].style[pBoxShadowProperty] = "";
Here is the jsfiddle, press the light green button 'rght' on top right.

I think I figured out what your issue is. You use here:
iActiveNo = iActiveNo + 1;
something that has not been defined in your posted code. However you do have:
var iActive = 0;
which I think should have actually been:
var iActiveNo = 0;
otherwise your code has JS error in it (as it is posted, anyway).
Other than that (that is, if your intention was to take the 1st <li> element out of the <ul> element and remove its box-shadow CSS property) - your code is just fine.
Edit
Dude, what a mess.. :) Here is a JSFiddle I fixed up a bit. Below is the explanation.
There are several things going on in that JSFiddle that should be fixed before we get to the real problem.
You have errors in that fiddle - see console. The line:
var pBackgroundColorProperty = eStyle.backgroundColor //[pBoxShadowProperty];
doesn't end with a semicolon, and is then interpreted as a function due to (..) on the next line (I think) - which (for me at least) results in an error in JS console. If semicolon is added - error is gone.
Additionally... There is a line:
console.log (eTabInd.children[iActiveNo-1].style.pBoxShadowProperty);
which prints your undefined and is exactly what was discussed below and should be
console.log (eTabInd.children[iActiveNo-1].style[pBoxShadowProperty]);
which then prints the empty string.
Moreover, when printed, your pBoxShadowProperty variable contains boxShadow string. Which is, of course, not a valid CSS property I am familiar with. So this:
eTabInd.children[iActiveNo - 1].style[pBoxShadowProperty] = "";
won't do a thing.
Now to the meat of the issue here...
eTabInd.children[iActiveNo-1].style
doesn't have 'box-shadow' property to begin with, because you haven't put it in style attribute of <li> element. It is put on the <li> element through the virtues of this CSS selectors sequence: #feature_tabs_indicators ul #ind_bt.
Now, since you wanted the style attribute - you won't get the computed style the above CSS selectors sequence applies. Thus - you won't be able to remove it.
What you could have done is create another class, that doesn't have a box-shadow property and replace your original c_ind with it.

it looks like you have not set value correctly as it should be like
eTabInd.children[iActiveNo - 1].style.pBoxShadowProperty = "";
Dose that help or dose still return 0?

Related

Using CSS classes as jQuery variables

To my understanding, in jQuery you can set a variable then use it later down the line.
var x = 2;
function add() {
x = x + 2;
}
In my code, I have a CSS class as a variable.
var scroll = $('.verticalScrollBar');
function changeScrollBar() {
...
TweenMax.set(scroll, {css:{height:scrollBarHeight+'%'}});
...
}
However, this does not seem to work. Initially, I thought it might have been a problem with GSAP TweenMax so I tried the following code.
function changeScrollBar() {
...
TweenMax.set($('.verticalScrollBar'), {css:{height:scrollBarHeight+'%'}});
...
}
To my surprise this did work. So my question is why does the line of code TweenMax.set($('.verticalScrollBar'), {css:{height:scrollBarHeight+'%'}}); work but this line of code TweenMax.set(scroll, {css:{height:scrollBarHeight+'%'}}); does not?
Working example JSFiddle
Broken example JSFiddle
Changing:
var scroll = $('.verticalScrollBar');
To:
var scroll = '.verticalScrollBar';
Results in your broken example operating as expected.
.....
Edited (twice now) for an explanation as to why this is happening.
As #Katana314 pointed out, the var scroll bit is defining a jQuery object that doesn't exist at this point. $(".verticalScrollBar"); doesn't exist until after changeScrollBar() is executed.
Using var scroll = '.verticalScrollBar' works because you're not declaring a nonexistent jQuery object. You're simply providing a bit of text, which CAN be matched to the object once it has been created.

One function for many buttons

I have dynamically created elements on the page, a picture and three buttons which are created upon clicking the main button.
All of this works, but now I am trying to change the display on the dynamically created div with the pics to "none".
More than one issue arises here for me, first I cannot find out how to make the div "images" the target, or select it.
I am trying to get one function to do this for all the elements, they are all structured equally just the pictures are different.
function hidePic(arrayPos){
var elem = document.getElementsByClassName("closingButton") + "[" + arrayPos + "]",
finalTarget = elem.getElementsByClassName("images")[0];
finalTarget.style.display = "none";
}
document.getElementsByClassName("closingButton")[0].addEventListener("click", function(){
hidePic(0);
});
This is the relevant code, lines 4 to 10. If this is commented out, the rest of the code works, but as it is I get entirely unrelated errors in dev Tools.
Click this link to see Codepen.
So the question is, how can I best implement the above code?
So just working on the code above you can do this in order to make it work for all instances. First let me point out that this:
var elem = document.getElementsByClassName("closingButton") + "[" + arrayPos + "]";
will never work. That line is building a string. What you really want to make that line work is:
var elem = document.getElementsByClassName("closingButton")[arrayPos];
But even that I find unnecessary. Take a look at this code.
function hidePic (elem) {
var finalTarget = elem.getElementsByClassName("images")[0];
finalTarget.style.display = "none";
}
var closingButtons = document.getElementsByClassName("closingButton");
var index = 0, length = closingButtons.length;
for ( ; index < length; index++) {
closingButtons[index].addEventListener("click",
function () {
hidePic(this);
}
);
}
This first finds all elements with the class closingButton. Then for each one we attach a click event listener. Instead of attempting to pass some index to this hidePic function we already have our function context which is what you seem to be trying to find in the function so lets just pass that and use it to find the image inside.
Let me know if you have any questions. I took a look at your codepen as well. I am not sure you should be forcing all that interactive HTML into a button element honestly, which itself is considered an interactive element. Not sure that meets the HTML spec. Perhaps add that HTML below the button. I bet when you click on things inside of that button it will register as clicks on the button as well unless you remove the event upon inserting your elements but then it seems like its getting too complicated for the simple stuff you are trying to do here.
The codepen complains because there is no element with the "closingButton" class, so it's trying to call addEventListener on nothing, but I'm doubting that's the actual error you're seeing.
It's also worth nothing that I think this:
var elem = document.getElementsByClassName("closingButton") + "[" + arrayPos + "]",
is excessive.
var elem = document.getElementsByClassName("closingButton")[arrayPos];
should be sufficient. Also not the syntax error at the end of the same line: it should be ; not ,. If this is the error in your code it could explain why you were getting "unrelated errors" syntax errors can cause misleading problems that are supposedly in other areas of the code!
Lastly, I'd highly recommend using JQuery to do your selection magic - it's exactly what it was designed for. If you're averse to using JS libraries, fair enough, but it would make your code a lot simpler and you can have reasonable confidence that it will perform the tasks about as optimally as is possible.

variable turns to undefined inside a for loop

I'm am battling with a javascript function I'm working on.
Inside a for loop I'm iterating all elements with class "visible", inside that loop
I'm preforming two actions.
elements[i].removeAttribute("class");
elements[i].setAttribute("class", "hidden");
For some reason only 1 is valid. 2 produces an error saying:
Uncaught TypeError: Cannot call method 'setAttribute' of undefined
Even when I log elements[i] using console.log; after the first console.log call
the element exists, but on the second console.log elements[i] is 'undefined'
What the hell am I missing here, this is driving me crazy, if my laptop wasn't so expensive
it would have been broken by now. Help :(
Here's the function:
function hide_visable_elements()
{
// remove body EventListener
var body = document.getElementsByTagName("body");
body[0].removeEventListener("click", hide_visable_elements, true);
var elements = document.getElementsByClassName("visible");
for (var i = 0; i < elements.length; i++)
{
console.log(elements[i]); // Works like a swiss clock
elements[i].removeAttribute("class");
console.log(elements[i]); // why elements[i] is 'undefined' now ???
elements[i].setAttribute("class", "hidden"); // << turns to useless code
}
}
This is because getElementsByClassName returns a NodeList, which is live. That is, it updates itself when the elements it refers to change.
When you remove the class attribute from an element in the NodeList, it gets removed from that list (since it no longer has the visible class name).
You don't actually need to remove the attribute. Just setting it will do the job just as well. But since the NodeList is changing as you manipulate the elements it contains, you need to count backwards through it (as each time you change one element of it, it is removed so the length decreases by one):
for (var i = elements.length - 1; i >= 0; i--) {
elements[i].setAttribute("class", "hidden");
}
getElementsByClassName is a live NodeList so changing className of the items immediately affects whole list. I would recommend use querySelectorAll insead.
Plus instead of var body = document.getElementsByTagName("body"); use document.body.
I think that the problem is elements[i].removeAttribute("class"); since you selected the element using a class getElementsByClassName("visible"); . I think so when you remove class attribute completely from the element things are going wrong.
Try some tweak with the code. You are not suppose to remove attribute class if you are planning to use the same element which is selected using class attribute.

jQuery width() not working

I'm trying to get the width of the first div of the specific class "span4" on my Bootstrap site, but the script simply fails to execute the second line where I call width(). Here's what I have:
var span = $('div.span4').first();
spanWidth = span.width();
The strange part of this is that I have similar working code immediately after that works fine when I remove the above two lines and set spanWidth to a constant:
elements = $('a.backlink');
elements.each(function() {
var a = $(this);
if (a.width() > spanWidth) {
var aText = a.text();
var lastIndex = aText.lastIndexOf(' ');
var aTruncated = aText.substring(0, lastIndex);
a.text(aTruncated + '...');
}
});
Any idea what might be causing this? I've tried a lot of different ways to format those two lines differently, such as switching to an each() method, condensing to one line, and using [0] and get(0) instead of first().
Try to set the span's display to inline-block.
#SimonM's comment led me to try replacing my implied global spanWidth with an explicit global window.spanWidth, and now everything works. Thank you!

Javascript retrieve element by it's properties

Every HTML element has offset values. Can I return an element that has, for example, offsetLeft > 10?
Have never heard of this feature, therefore the question.
I'm aware that this can be done with loops, but those are slow. Had an idea about XPath, but cannot find anything related to properties within reference.
Thanks in advance!
P.S. No need for outdated browser compatibility- HTML5'ish can do.
As far as I'm aware, there is no way to do this that does not involve looping of some form. You could do it in standard JS with something along these lines:
var elems = document.getElementsByTagName("*"),
myElems = [];
for(var i = 0; i < elems.length; i++) {
if(elems[i].offsetLeft > 10) myElems.push(elems[i]);
}
Or, if you're using jQuery you can do it with a little less code (but it's probably even slower!):
var myElems = $("*").filter(function() {
return $(this).offset().left > 10;
});
If you think about it, you want to select all of the elements in a document with a certain property value. That's always going to involve a loop at some point, whether you write it yourself or not, as every element has to be checked.
Have you looked at this page yet? offset
jQuery can easily select attributes of elements
<div>Dont find me</div>
<div this="yes">Find me</div>
$('div[this=yes]'); // will select the second div
The problem you are going to run into is things like offset and position are calculated values, and not stored in the dom with the elements upfront. If you need to select by this, I would suggest putting them as attributes inside of the dom element itself. Then the above method with work just fine.
I would suggest the best way to do this would be to extend jQuery's selectors. Something like this works well:
$.extend($.expr[':'],{
offsetLeft: function(a,i,m) {
if(!m[3]||!(/^(<|>|=)\d+$/).test(m[3])) {return false;}
var offsetLeft = $(a).offset().left;
return m[3].substr(0,1) === '>' ?
offsetLeft > parseInt(m[3].substr(1),10) :
m[3].substr(0,1) === '<' ? offsetLeft < parseInt(m[3].substr(1),10) :
offsetLeft == parseInt(m[3].substr(1),10);
}
});
This would allow you to select elements using syntax such as
$('span:offsetLeft(>10)')
or
$('.someClass:offsetLeft(<10)')
or even
$('.someClass:offsetLeft(=10)')
Live example: http://jsfiddle.net/X4CkC/
Should add that this hooks into jQuery's selectors which are generally quite fast, but no doubt somewhere deep within there is a loop going on. There' no way of avoiding that.
You can easily do it with jQuery
$("*").each(function(index, elem){
if($(this).offset().left > 10){
// do something here with $(this)
}
});

Categories

Resources