Not changing image src attribute through javascript - javascript

The following is my current JavaScript code which is not working. I'm trying to change the image.
function imgchange(a)
{
var e=document.getElementById(a);
if(e.src == "plus.png")
{
e.src = "minus.png";
}
else
{
e.src="plus.png";
}
}

When you are using img.src it returns whole path to img src, not only plus.png
You have to make comparison like http://localhost/images/plus.png (whatever your path is)
or use getAttribute method like is mentioned in undefined's post

src property includes the full url of the image, try using getAttribute method, which returns the specified value in the HTML.
if ( e.getAttribute("src") === "plus.png" )
Note that means that you should also set the new value using .setAttribute() for future comparisons. If you want to use the .src property you should either compare the full paths or use other methods like regular expression or split method:
if ( e.src.split('/').pop() === "plus.png" )

the function itself should be working. there must be something wrong at the place you are calling imgchange("xyz").
so maybe you can show us the code where the function is actually called.

function imgchange (a) {
var e=document.getElementById(a);
if (e.src.replace(/.+\//, '') === 'plus.png') {
e.src = "minus.png";
} else {
e.src="plus.png";
}
}
Should work.

Related

jQuery checking if input has value by function call

I defined this function which checks if a input field is empty or not
function hasValue() {
if ( !!$.trim( $(this).val() ) )
return true;
}
It just works fine for filtering a jQuery Collection
$('#form').find( '.email' ).filter( hasValue );
But I also want to reuse the hasValue()-function for toggeling a class.
$('.input').change( function () {
var empty = hasValue().apply( $(this) ); //throws an error
// var empty = $(this).hasValue(); //doesn't work either
$('#box').find('.required-tmp').toggleClass('required', empty );
});
Any help is appreciated.
Juste pass this to apply. And don't execute () before :
hasValue.apply(this);
If you want to have a better use of your function it must accept an element as parameter it's not a good pattern to use this like this. Prefer to pass the element in arguments
function hasValue(elem) {
return !!$.trim($(elem).val());
}
And then the usage is the same for map :
$('#form').find( '.email' ).filter( hasValue );
And :
$('.input').change( function () {
var empty = hasValue(elem); //throws an error
$('#box').find('.required-tmp').toggleClass('required', empty );
});
$('.input').change( function () {
var empty = hasValue.apply( $(this) ); //apply function should be used this way .
// var empty = $(this).hasValue(); //doesn't work either
$('#box').find('.required-tmp').toggleClass('required', empty );
});
Why are you using double negation in the hasValue method?
Secondly, use apply as:
var empty = hasValue.apply( this );
This will pass the element as the parameter which you can use there!
Thirdly, for checking whether the value exists, you can just use type check instead of trim as:
if(typeof $(this).val !== 'undefined'){
return true;
}
See if that works for you!
fn.apply syntax should be like hasValue.apply( $(this) ) instead of hasValue().apply( $(this) )
May be that will solve your problem.
Thanks & Regards,
Charly

Getting a submit selection into a variable with jQuery

I am making a condition that verifies some class and depending on the value, the respectable submit input is stored into a variable:
_btnAjax = "";
if (_aVar.hasClass("one")) {
_btnAjax = $("#one");
}
if (_aVar.hasClass("two")) {
_btnAjax = $("#two");
}
and then, using the .on('click' function(e){}); on that variable:
_btnAjax.on('click', function(e) {
// some Ajax
}
The problem is that I receive the error TypeError: _btnAjax.on is not a function
I already made exactly the same thing on a <li></li>, but either <button></button> or <input type='submit'/> don't work.
The reason that fails is because neither of your two conditions are true.
For example, if _aVar does not have a class of one AND it does not have a class of two then _btnAjax is a string in your code.
Double check that your UI has the right classes.
In addition, make sure you handle the other case.
Try writing your code more like this:
var _btnAjax;
if (_aVar.hasClass("one")) {
_btnAjax = $("#one");
} else if (_aVar.hasClass("two")) {
_btnAjax = $("#two");
} else {
// Do something to handle the fact that neither case was true.
// You can return early, throw an error, or set _btnAjax to
// an empty jQuery object.
}
You're trying to use a jQuery function (.on)
Try this:
$(_btnAjax).on('click', function(e) {
// some ajax
}
Although I think you should use a id or class selector like:
// selector should be whatever your button has as id or class property.
// (preferably an id since this won't conflict with other classes)
$('#btnAjax').on('click', function(e) {
// some ajax
}

pure javascript onclick becomes undefined after assigning functions scope ends

It's been a while since I wrote Javascript without jQuery, so please bear with me. I'm assuming I'm just doing something silly. I have this function that converts link urls to an internal representation that I use with a router I wrote.
Templater.prototype.replace_links = function() {
this.links = document.getElementsByTagName("a");
for (i = 0; i < this.links.length; i++) {
if (!(this.links[i].getAttribute("href") === this.VOID && this.links[i].getAttribute(this.HREF))) {
this.links[i].setAttribute(this.HREF, this.links[i].getAttribute("href"));
this.links[i].setAttribute("href", this.VOID);
this.links[i].onClick = function(self, link) {
return function() { self.router.go(link.getAttribute(self.HREF)); };
}(this, this.links[i]);
}
}
}
This function is called the first time when Templater is initialized. The first time it works correctly. However, I run it a second time after I append some html into the body of the document. I run it again just in case that appended html has links in it too:
<body>
<!-- arbitrary new html is loaded in here -->
Login <!-- becomes Login correctly -->
Home <!-- becomes Home correctly -->
</body>
When I console.log(this.links[0], this.links[0].onClick) after the function has been run but still within a Templater function, I get the correct html and then undefined for the onClick event:
Discover undefined
When I log the same to values within the replace_links scope, I get what I'm expecting. I.e. the function is shown:
Discover function () { self.router.go(link.getAttribute(self.HREF)); }
I was playing around with it some more and tried this way and got the same kind of thing.
Templater.prototype.replace_links = function() {
this.links = document.getElementsByTagName("a");
for (i = 0; i < this.links.length; i++) {
if (!(this.links[i].getAttribute("href") === this.VOID && this.links[i].getAttribute(this.HREF))) {
(function(self, link) {
link.setAttribute(self.HREF, link.getAttribute("href"));
link.setAttribute("href", self.VOID);
link.onClick = function() { self.router.go(link.getAttribute(self.HREF)); };
})(this, this.links[i]);
}
}
}
I console.log after the replace_link scope ends like before and this time I still get:
Discover undefined
I'd really appreciate any help and/or suggestions! Please let me know if I'm missing anything helpful.
The key points here have been treated as minor details.
I append some html into the body of the document
and
this.links[i].onClick = function(self, link) {
My point is, if you alter innerHTML, which I assume is the way you "append some html into the body of the document," the browser will serialize the DOM objects into HTML, do the string concatenation, and then parse it again. This results in new objects which no longer have the expandos, such as onClick. onClick is a custom property; you probably meant onclick anyway.
However, some of your changes will be serialized and parsed successfully, namely the setAttribute operations. Thus, when you run replace_links after the HTML appending, the
if (!(this.links[i].getAttribute("href") === this.VOID && this.links[i].getAttribute(this.HREF)))
check will treat the link as already replaced and not assign the onClick again.
Here's a fiddle that shows this in action. http://jsfiddle.net/k9d7b2ds/
UPDATE: Made some additional changes. The onclick event's default this object is always referencing the window object. You need to pass over the closure.
Check sample code here:
http://jsfiddle.net/y0443fz6/
Templater.prototype.replace_links = function() {
var that = this;
this.links = document.getElementsByTagName("a");
for (i = 0; i < this.links.length; i++) {
if (!(this.links[i].getAttribute("href") === this.VOID && this.links[i].getAttribute(this.HREF))) {
this.links[i].setAttribute(this.HREF, this.links[i].getAttribute("href"));
this.links[i].setAttribute("href", this.VOID);
this.links[i].onclick = function(self, link) {
return function() {
self.router.go(link.getAttribute(self.HREF));
};
}(that, this.links[i]);
}
console.log(this.links[i], this.links[i].onclick);
}
}
hope that helps. gl

Getting class-based CSS attribute via JS not working

I have JS code where I try to determine if the size of an image was explicitly set via CSS or the by using the width/height attributes. If so, I respect the set sizes, otherwise I execute code to size the image myself. Here is the code. It uses some jQuery, so image is a jQuery object and image[0] accesses the actual DOM element:
if (image.attr('height') == undefined && image[0].style.height.length == 0) {
image.data("explicit_height", false);
//Do stuff to the image size here
} else {
image.data("explicit_height", true);
}
// ...SAME FOR WIDTH...
The problem I am facing is that this code works fine as long as either the height/width or style attributes are used. When the height/width is set via a CSS selector (for example a class selector), the code is NOT working. The code is executing on window load.
Anyone got an idea why this happening and how to fix it?
You have to use getComputedStyle or currentStyle to get styles set in a stylesheet (the latter works in earlier versions of IE.) style will only return inline style properties.
(image.getAttribute('height') == null
&& ((window.getComputedStyle(image)||image.currentStyle)['height']) == '0px') ?
//do stuff
:
//do other stuff
You could also map the evaluation of that statement to an object of functions:
function foo() { image.data("explicit_height", false) }
function bar() { image.data("explicit_height", true) }
var bool = ( image.getAttribute('height') == null
&& ((window.getComputedStyle(image)||image.currentStyle)['height']) == '0px' ),
fn = {
true:foo,
false:bar
}[bool]();
You'll need to play around with image.getAttribute('height') == null and the statement that follows that, because they are based on default values in the browser, which could be different (I only tested on Firefox 26 for Mac 10.9, and the default values where there is no height attribute is null and where there is no height declared in a stylesheet is 0px.)
Updated Answer
Check out this jsfiddle:
var image = document.getElementsByTagName('img')[0];
function foo() { image.setAttribute("name", "foo") }
function bar() { image.setAttribute("name", "bar") }
var styleSheets = document.styleSheets;
function isHeightDefinedInStyleSheets(tag) {
for(i in styleSheets) {
var rules = styleSheets[i]['cssRules'||'rules'];
if(rules!==undefined) {
for(j in rules) {
if(rules[j]['style']!==undefined&&rules[j].selectorText===tag) {
if(parseInt(rules[j]['style']['height'])>0) {
console.log(true);
return true;
}
}
}
}
}
console.log(false);
return false;
}
var fn = {
true:foo,
false:bar
}[!isHeightDefinedInStyleSheets('img') && image.getAttribute('height') == null]();
This function will check the stylesheets themselves for whether a rule exists (which is different than getComputedStyle, which will get the style of the element based on its computed value, regardless of whether the value was defined inline or in a stylesheet, etc.) Its return value is passed along as a key with the return value checking whether height was defined inline as an attribute to the image. If the statement evaluates "true", it will execute the "true" function (foo) in the object fn; otherwise it will execute bar.
I haven't dealt with the styleSheets property before, so the code needs to be cleaned up quite a bit, but it demonstrates the basic point. Also, if you have a collection of img elements, you'll need to implement a looping function to iterate through each element in the collection.

Bind Javascript Objects to HTML elements

I just want to share an experience with you all. So my problem was, that I came across the problem of binding javascript back-end objects to HTML front-end elements. Now, i have searched through google and read some stackoverflow articles about this problem, and many posts answer to this is to use jQuery.data(), however in my first attempts I did not succeed because there was something I did not know about jQuery's object creation method. My problem was that, I wanted to retrieve the stored data outside of the scope where i stored this, and jQuery always(i think) returns a new object reference when i write jQuery('selectorID'). So for example:
var ref1 = $('#myID');
var ref2 = $('#myID');
if(ref1 == ref2)
{
alert(true);
}
else
{
alert(false);
}
Will always alert false. However replacing the jQuery method with javascript's built-in getElementById() we will get the same reference! Hence the following code will always alert true!
var ref1 = document.getElementById("myID");
var ref2 = document.getElementById("myID");
if(ref1 == ref2)
{
alert(true);
}
else
{
alert(false);
}
The little morale of my story is that if you want to globally bind javascript objects to HTML elements and are thinking about using jQuery's data() method, store the data on the reference returned by javascript's built-in getElementById(). That way, whereever you retrieve the reference with getElementByID, you will always get the same reference and can get the data from it using jQuery's data() method.
My Questions:
Is my logic of thinking ok?
Is there a better way to globally bind javascript objects to HTML elements?
Whatever the reason behind the code you mention not working was, it was decidedly not the fact that jQuery gives you a new collection for every query. Given the HTML:
<div id="somediv"> foo bar </div>
the following Javascript works as expected:
var $ref1 = $('#somediv');
var $ref2 = $('#somediv');
console.log("$ref1:", $ref1);
console.log("$ref2:", $ref2);
// compare the collections / references
console.log("$ref1 == $ref2:", $ref1 == $ref2); // => false
console.log("$ref1 === $ref2", $ref1 === $ref2); // => false
// compare the referred DOM elements themselves
console.log("$ref1[0] == $ref2[0]:", $ref1[0] == $ref2[0]); // => true
console.log("$ref1[0] === $ref2[0]", $ref1[0] === $ref2[0]); // => true
$ref1.data('somedata', 'SOMEDATA');
console.log('$ref1->somedata:', $ref1.data('somedata')); // => SOMEDATA
console.log('$ref2->somedata:', $ref2.data('somedata')); // => SOMEDATA
The way I do it is something like this.
var ref1 = $('a');
var ref2 = $('div');
var equal = ref1.length === ref2.length;
if (equal) {
$.each(ref1, function(i) {
equal = equal && ref1[i] === ref2[i];
if (!equal) {
return false;
}
});
}
if (equal) {
alert(true);
} else {
alert(false);
}
ref1[0] === ref2[0] // Should return true
I think jQuery's instances are unique, so you can compare its matched items, which should be just one element when you reference an ID.
You could do something like this:
var ref1 = $('#myID')[0];
var ref2 = $('#myID')[0];
I dive into jQuery's source code, and find the constructor of jQuery. As follow:
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
}
Whenever you use ref2 = $('#myID') to retrive a corresponding jQuery element, jQuery will create a new object to you. So the == will return false to you coz' the two element is completely different to js object engine.
Seems getElementById method is more fit your need. But I don't know how js engine perform its getElementById method.

Categories

Resources