jQuery cannot change a value where JS can because the library is out of date
Simple answer:
The jQuery library my code base was using was out of date.
If yours is up to date try the following:
Step though the unminified version of jQuery to see if the issue is inside the library (which 9 times out of 10 it probably will not be)
When all else fails, just write a pure javascript solution.
Sometimes when I am writing a "class" in javscript with jQuery, jQuery will just not 开发者_StackOverflow中文版function as it should. For example today I was doing the following on an input select:
$(this).val(newValue);
This was working in jFiddle just fine, but not working in my project (both had 0 script errors). I tried to isolate the code as much as possible, but to no avail I could not get it to work.
The "solution" for this was to just write straight up javascript to set the value and it worked just fine.
I am not a jQuery master, but this is not the first time I had to write a straight javascript solution when jQuery "failed." Do any jQuery masters out there know why something like this might happen? Is there a method of debugging jQuery I am not aware of? Does anyone else run into these types of problems with jQuery? If so, do you have a solution?
Update(s):
Aug 25, 2011
I downloaded the most recent version of the jQuery library and stepped through it only to find that the bug was probably resolved between jQuery 1.4.4 and 1.6.2. I didn't realize the person who handles most of the javascript wasn't keeping the jQuery library up to date. After stepping through the jQuery 1.4.4 library it seems that jQuery was not able to find my selector for some reason and therefore was never setting the value. This problem has been resolved in 1.6.2....
Lesson of the week... keep your jQuery library up to date and verify your senior developer's statements when things aren't matching up.
-
I chose the answer I did because it was the only one which really helped me diagnose the source of the problem. While stepping through the unminified version of jQuery is such a simple solution, I actually should have done that before posting this question. I will post my findings as to why it didn't work later.
Aug 24, 2011
I agree with the user Sohnee that having a duplicate 'name' isn't bad practice and in some places you actually need it.
I feel rather stupid because the posted code has been wrong for almost a week now. I have updated the code. I moved the init function to the public scope.
Aug 22, 2011
While I am partially satisfied that the core of the problem is with duplicate names, I need to know why are duplicate names bad?
I know when dealing with inputs/css/etc you usually except IDs to be unique and classes to represent groups. I didn't think there were any rules about names, but if someone can explain to me why having duplicate names is bad practice, I will consider that the answer to this problem.
Aug 16, 2011
As for the current answers, I don't think it is a conflict. jQuery is working just fine. The binds are triggering causing the functions to be called in both implementations.
The problem line is
$(overrideSelector).each(
function(){
$(this).val(newVal);
}
);
For example, if I console out newVal
in the .each() it will have a value of lets say 'A'.
$(this).val()
it will be 'B'. Then $(this).val(newVal);
is run. After that if I do a console log of $(this).val()
it will still be 'B'.
In the comments someone mentioned that I might be using the word this
wrong. Both of these returned 0 javascript errors on Chrome's console. I will give the following code snippet was what was having problems. I will write the original and then what I replaced it with javascript.
I am aware the name is the same, but that is ok. The page I am working on is a huge form and the cloned select is just to make the user not have to scroll back to another part of the form.
HTML:
<div id='overrideHolder'>
</div>
<select name='mySelect'>
<option value='A'>A</option>
<option value='B'>B</option>
</select>
jQuery (this does not work):
var someClass = (function(){
var overrideSelector = '[name="mySelect"]';
...
return{
init : function(){
$(overrideSelector).clone().appendTo('#overrideHolder');
$(overrideSelector).each(
function(){
$(this).bind(
'change',
{},
function(){
someClass.overrideTryAgain(this);
}
);
}
);
}
overrideTryAgain : function(element){
var newVal = $(element).val();
$(overrideSelector).each(
function(){
$(this).val(newVal);
}
);
...
},
...
}
})();
$(document).ready(function(){
someClass.init();
}
Javascript (this works):
var someClass : (function(){
var overrideSelector = '[name="mySelect"]';
...
return{
init : function(){
$(overrideSelector).clone().appendTo('#overrideHolder');
$(overrideSelector).each(
function(){
$(this).bind(
'change',
{},
function(){
someClass.overrideTryAgain(this);
}
);
}
);
}
overrideTryAgain : function(element){
// NOTE: Using javascript instead of jQuery because jQuery was not duplicating the selected value properly
var newValue = $(element).get(0);
newValue = newValue.selectedIndex;
$(overrideSelector).each(
function(){
var currentSelect = $(this).get(0);
currentSelect.options[newValue].selected = true;
}
);
...
},
...
}
})();
...
$(document).ready(function(){
someClass.init();
}
I tried to reproduce the problem from your code, and like others could not do so. So it seems like there's something external that is interfering: a conflict, something with the markup, but generally something that's not obvious.
This sounds like a job for source-code stepping. Run this against the non-minified jQuery and trace the offending line with a debugger such as the built-in Chrome debugger or Firebug to see what's happening inside jQuery. This should reveal what's going wrong, be it a jQuery bug or something in your own code/markup that you didn't see before.
You have to make sure you are picking the right element. For that you can use Chrome console.log
to see which DOM node you are trying to set the value.
Duplicated names on HTML are bad for a simple reason. When a form is sent to the server the input elements inside it are submitted sending the values through an object whose keys are the DOM names. If you have duplicated names the object will not bind the values with unique keys. You may have same names in different forms, that is allowed.
You can check the html specs for Form Controls (w3.org) to get more information on Control Names and the behavior for checkboxes.
Of note for a WebForms application: the whole page is a form, don't use same names ever...
First of all make sure, the jquery library is included. then try using jQuery
object instead of $
some libraries use $
in their code. also take a look at jQuery.noConflict()
method:
http://api.jquery.com/jQuery.noConflict/
If the purpose is simply to clone and "synchronise" values between selects your solution seems to be a bit too complex for what it is. First of all you probably do not need all the each loops as the $(overrideSelector) will do that for you automagically. Anyhow here is a simplified version of your code (If I missed the purpose of what your code is supposed to do let me know)
var someClass = new function() {
var overrideSelector = '[name="mySelect"]';
this.init = function() {
$(overrideSelector).clone().appendTo('#overrideHolder');
$(overrideSelector).live("change", function() {
$(overrideSelector).val($(this).val())
})
}
}
$(document).ready( function() {
someClass.init();
})
regarding the update, this always seems more solid to me:
$(overrideSelector).each(
function(i, element){
$(element).val(newVal);
}
);
I was looking at this problem and I couldn't get it to work with your code, but I tried implementing it on my own...
Will this work for you?
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Untitled Page</title>
</head>
<body>
<div id='overrideHolder'>
</div>
<select name='mySelect'>
<option value='A'>A</option>
<option value='B'>B</option>
</select>
<input id="addSelect" type="button" value="Add select" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript">
(function ($) {
var _constants = {
selector: 'select[name="mySelect"]'
};
var _methods = {
init: function () {
var $select = $(_constants.selector).live('change', function (e) {
var $this = $(this);
var val = $(this).val();
$(_constants.selector).not($this).val(val);
});
_methods.cloneSelect($select);
$('#addSelect').click(function (e) {
_methods.cloneSelect($(_constants.selector).eq(0));
});
},
cloneSelect: function ($select) {
$select.clone().appendTo('#overrideHolder');
}
};
//shorthand for $(document).ready
$(function () {
_methods.init();
});
})(jQuery);
</script>
</body>
</html>
There is something wrong at a deeper level.
- You can't simply clone the select, you shouldn't have two elements with the same name
- Treat the original and the clone separately, you don't need to listen for changes on the original or reset the value on the copy
- Drop the .each calls, jQuery already does that for you internally
- $(element).get(0) === element. You're putting it in then taking it out.
That said, you can try something along Michal's suggestion:
var mySelect = $(overrideSelector)
var mySelectClone = mySelect.clone().appendTo('#holder')
mySelectClone.bind('change', function(){
addressValidation.overrideTryAgain(this)
})
...
overrideTryAgain : function(element){
var newVal = $(element).val()
mySelect.val(newVal)
}
Also, have you compared the two versions's outputs?
overrideTryAgain : function(element){
var newVal = $(element).val()
var newVal2 = element.options[element.selectedIndex].value
console.log("jQuery: "+ newVal)
console.log("DOM: "+ newVal2)
$(overrideSelector).val(newVal);
...
},
And if your option values include empty strings or other falsy values ("0", etc), you're in for a bag of hurt.
I have seen duplicate named form elements cause problems, so I would try to eliminate those.
I would also recommend being very careful with the val()
method in this case. It looks fine in your code, but it's easy to stare at some code expecting on thing, forgetting that val()
only returns the value of the first matching element (in the DOM).
I tried to duplicate your problem, but couldn't really. There are quite a few simplifications you can make that may reveal the problem:
- use
change
instead ofbind('change',{}
- eliminate
each
calls pass the
newVal
into youroverrideTryAgain
method-- instead of an element. The element is not needed and this will make it easier to test/debug.var selector = "[name=mySelect]"; var holder = "#overrideHolder"; ... $(selector).clone().appendTo(holder); ... $(selector).change(function () { overrideTryAgain($(this).val()); }); var overrideTryAgain = function (newVal) { $(selector).val(newVal); }
Is it bad to have duplicate 'name' in different HTML input tags? NO - If this was the case then radio buttons would suck.
jQuery cannot change value where JS can NO
Your issue lies outside the code that you have supplied. jQuery has no issues with performing your requirements, and without the complete code it is virtually impossible to pin point - which works perfectly fine.
This statement is backed by the following fiddle http://jsfiddle.net/EeQEN/ which is just a simplification of the code provided.
var overrideSelector = '[name="mySelect"]';
var overrideTryAgain = function(element) {
var newVal = $(element).val();
$(overrideSelector).each(function() {
$(this).val(newVal);
});
};
$(overrideSelector).clone().appendTo('#overrideHolder');
$(overrideSelector).each(function() {
$(this).bind('change', {}, function() {
overrideTryAgain(this);
});
});
I have tried all the recent versions of jQuery, and unless it is a bug with a particular browser (which you have not specified), then I suspect the problem lies with variable scope leek.
If you are unconvinced then please provide a functional example of the problem an I am sure the exact problem can be identified.
A piece of advice for future reference, don't requery the DOM; cache all selected elements and never apply events via looping thru the elements - use delegate and allow events to bubble up, not only will it improve the performance it will also make your code more maintainable.
It is actually valid to have the same name attribute on many form elements, this is actually how some form elements are designed to work, such as radio buttons. It is also how some server-side languages are designed to work, for example the array-style naming in php name="country[]"
It is not bad practice to have duplicate names - you just need to be aware that you are not dealing with a unique element when you get the element by name. In fact, document.getElementsByName("sharedName")
returns an array of results, even if you only have one element with the specified name.
So this isn't a question of it being bad practice, it is a question of whether your code (or the code contained in a framework you are using) understands this non-uniqueness.
I think you might have some luck implementing jQuery Class.
I put the classes you used in jsfiddle and even then it came up with a bunch of problems. The parentheses aren't closed everywhere and then you tried to define methods inside of a function when they aren't chained together.
var someClass = function(){
...
})();
Is missing a parentheses left of "function" and try putting in jQuery in the ending parentheses.
var someClass = (function(){
...
})(jQuery);
Along with that, I don't think you can define the normal "methods"
init : function(){
overrideTryAgain : function(element){
Just like that inside that parent function.
精彩评论