jquery if (value)... else avoiding "duplicate selector" - javascript

I have a notification on PhpStorm that "Duplicate JQuery Selector" for a JQuery if...else conditional based on a value and then updating that value.
$(document).ready(function() {
$('#forgotten').click(function () {
if($('#button').val() === 'Login') {
$('#button').val('Send New Password');
}
else {
$('#button').val('Login');
}
return false;
});
});
PhpStorm tells me
Duplicated jQuery selector
Inspection info: Checks that jQuery selectors are used in an efficient way. It warns about duplicated selectors which could be cached and optionally about attribute and pseudo-selectors usage
Does this really matter? (I suspect not, it's micro-optimisation)
Is there a better way of writing this code (I suspect so)?
What is this better way?
Attempts:
if($('#button').val() === 'Login') {
$(this).val('Send New Password');
}
else {
$(this).val('Login');
}
The above code using this doesn't work; It seems that the JQuery isn't aware of what this is because it's not an invocation.
Attempt two is to use This useful answer:
$('#something')[$(this).val()=='X'?'show':'hide']();
But I can't see the syntax of how to apply this to swapping the value.
$('#button')[$(this).val() === 'Login'?$(this).val('Send New Password'):$(this).val('Login')];
The above attempt does not make the change expected (and does not give any console notices).
So, how do I do it; how do I write the original IF statement without repeating the reference?

Creating a jquery has (some, minor) overhead, so is not the most efficient. Your first code could be written as:
var btn = $("#button");
if (btn.val() === 'Login') {
btn.val('Send New Password');
}
else {
btn.val('Login');
}
while, in this case, it's a micro-optimisation, if you were doing this 1000s of times with very complicated selectors, it can save a lot of time to only run the jquery selection once.
Regarding your other attempts, using this will only make sense if this has relevant context, eg if inside an event:
$("#button").click(function() {
var btn = $(this);
...
In the case of .val() (and many other jquery functions (but not all)) a callback can be provided which is called for each entry in the jquery collection.
https://api.jquery.com/val/#val-function
A function returning the value to set. this is the current element. Receives the index position of the element in the set and the old value as arguments.
In your case, this becomes:
$("#button").val(function(i, v) { return v === "Login" ? "Send new" : "login" });
This will run the callback function on each item in the collection provided by the selector, so very useful if you want to run against multiple items with different results on each, example:
$(".button").val(function(i, v) { return v === "Login" ? "Send new" : "login" });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="button" class="button" value="Login"></input>
<input type="button" class="button" value="Login"></input>
<input type="button" class="button" value="Not Login"></input>

Whenever you call $("#button") jQuery searches the document and gets the corresponding DOM element. So you can try to assign the selection to a variable as followings to avoid the repetition of the search:
var btn = $('#button');
if(btn.val() === 'Login') {
btn.val('Send New Password');
}
else {
btn.val('Login');
}

Related

What's the proper way to check if an element has an ID or not with jQuery?

I thought I could check if an element has an ID with has() like this:
if ($button.has('#Menu-Icon')[0] !== undefined) {
//do stuff
}
but I learned that instead, it checks if there's a descendant which matches the ID. So I don't think this is the right jQuery method to use, but nevertheless it's the only thing that seems to show up when I search the subject on Google.
Which jQuery method is the right one for checking if a given element has a certain ID or not?
Check the id using the attr() function like this:
$('button').on('click', function() {
if ($(this).attr('id') == 'TheID') {
alert('The button you pressed has the id: "TheID"!');
} else {
alert('You pressed a button with another id..');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button id="TheID">TheID</button>
<button id="anotherID">anotherID</button>
You can check the id property like of the underlying element like
if ($button[0].id == 'Menu-Icon') {
//do stuff
}
or jQuery way to use .is()
if ($button.is('#Menu-Icon')) {
//do stuff
}
The has() method is used to filter a set of elements by checking whether those contains elements matching the passed selector, also it returns a jQuery object so checking for undefined will never be true
Try something like below which should work:
<div id=""></div>
$(document).ready(function(){
if($('div').attr('id') != ''){
alert("have id");
}else{
alert("do not have id")
}
});

Trying to dynamically validate fields with jQuery - custom function

I'm working on a validation function in jQuery and am getting a bit stuck. I've got a working version that is very lengthy and full of hardcoded form values which I'm trying to avoid. Here's what works, currently:
$(document).ready(function(){
var fname = $("#formFName");
var lname = $("#formLName");
fname.blur(validateFName);
lname.blur(validateLName);
function validateFName(){
if(fname.val().length > 0){
fname.removeClass("req_error");
return true;
}
else {
fname.addClass("req_error");
return false;
}
}
function validateLName(){
if(lname.val().length > 0){
lname.removeClass("req_error");
return true;
}
else {
lname.addClass("req_error");
return false;
}
}
});
That part works fine for those two fields but I'd like to encapsulate the whole thing in a function that's a bit easier to maintain. Something like this:
$(document).ready(function(){
$("#first_name").blur(validateField("first_name","text"));
$("#last_name").blur(validateField("last_name","text"));
$("#email_address").blur(validateField("email_address","email"));
function validateField(formfield, type){
var field = $("#" + formfield);
switch(type) {
case "text":
if(field.val().length > 0){
field.removeClass("req_error");
return true;
}
else {
field.addClass("req_error");
return false;
}
break;
case "email":
var filter = /^[a-zA-Z0-9]+[a-zA-Z0-9_.-]+[a-zA-Z0-9_-]+#[a-zA-Z0-9]+[a-zA-Z0-9.-]+[a-zA-Z0-9]+.[a-z]{2,4}$/;
if(filter.test(field.val())) {
field.removeClass("req_error");
return true;
else {
field.addClass("req_error");
return false;
}
break;
}
}
});
However, when I do that I get the following error in Firefox's JS error log:
Error: ((f.event.special[r.origType] || {}).handle || r.handler).apply is not a function
Source File: /scripts/jquery-1.7.1.min.js
Line: 3
A quick Google for that error hasn't yielded anything that means anything to me, unfortunately. I've tried alerts in various spots and doing so has verified that the proper form field names are indeed being passed where there supposed to be, but as I'm fairly new to jQuery (and not great at JavaScript) I'm at a bit of a loss here.
Thanks to anyone who can point me in the right direction. Also, if anyone thinks that I'm going about this in the wrong way, I'm more than happy to change. I tried using the jQuery validation plugin but I'm also using some dynamically created fields and unfortunately the plugin prevents the visitor from submitting the form when hidden required fields are involved.
Sounds like you could simplify this by attaching behavior instead of specific elements:
<input type="text" id="someId" class="validate-text" />
<input type="text" id="someOtherId" class="validate-email" />
$('.validate-text').live('blur', function()
{
$(this).removeClass('req_error');
if($.trim(this.value).length < 1)
$(this).addClass('req_error');
});
$('.validate-email').live('blur', function()
{
// Same thing with the email validation logic
});
This way, you dont need to attach specific event handlers to your elements and instead use those handlers to check the behavior you want. Then you simply mark HTML elements with specific classes to mark the validation mode you want.
Assuming I understood your question correctly.
You can't pass a function invocation expression to .blur(). You can pass an anonymous function constructor:
$("#first_name").blur(function () {validateField("first_name","text");});
Or pass the name of a function:
function validateFirstName() {
validateField("first_name","text");
}
* * *
$("#first_name").blur(validateFirstName));
#Tejs's suggestion to use classes to attach validators is a good one, as well.

Function show and hide form elements

I am trying to create a Jquery function to add or remove a drop down menu based on the selection of a different drop down menu,
This is my very first time trying to use Java script, and i need a little help to get it going.
here is what i have done up to now, but i cant seem to get it to work can somone tell me where i have gone wrong?
$(document).ready(function(){
$(function remove() {
$("select#name").val();
if (name == "United Kindom") {
$("select.county").show();
} else {
$("select.county").hide(); });
});
<select name="ab" onchange="remove();">
Firstly, what you're using is a mash-up of Javascript and jQuery(A Javascript Library). You must understand this. Just so you know the below is jQuery, and I'll add the pure JS to the end of the post.
Try This:
$(document).ready(function(){
$(".selectWrapper").on("change", "select#ab", function(){
$("select.county").toggle($(this).val() != "United Kindom");
});
});
<div class="selectWrapper">
<select id="ab">
This is also assuming you have
<select class="country">
somewhere in your code.
//---Pure JS ----
document.getElementById("ab").onchange = function(){
var selects = document.getElementsByTagName("select"), countrySelect;
for(var x in selects){
countrySelect = (selects[x].className == "country")? select[x] : "";
}
countrySelect.style.display = (this.value == "United Kindom")? 'none' : 'block';
};
Your code contains a syntax error, undeclared variables and a scope problem.
Your code can be rewritten more efficiently: $(fn) where fn is a function is equivalent to $(document).ready(fn). Also, the .toggle method can be used instead of if-then-show-else-hide.
Fixed code
The inline handler did not work either, because function remove was defined within the $().ready function.
To fix the code itself, bind the event handler using jQuery, instead of using an inline handler:
$(function() {
$('select[name=ab]').change(function() {
var name = $("select#name").val();
$("select.county").toggle(name == "United Kindom");
});
});
//<select name="ab">
Syntax error revealed
After indenting the code properly, the syntax error can be spotted quite easily
$(document).ready(function () {
$(function remove() {
var name = $("select#name").val(); // Prefixed "var name = "
if (name == "United Kindom") {
$("select.county").show();
} else {
$("select.county").hide();
} // <----------- This one is missing!
});
});
and just a quick hint at how to make easier use of jQuery
Remember, it's the "write less, do more" library:
// Easy jquery way to run when doc loaded
$(function() {
// you dont need to include the tag in an id selector
// remember, any way you can select an element in css, you can do in jQuery
$("#Country").change(function() { // here i use the .change event to know when this select box's value has actually changed to a new value
// i'm not sure exactly what you were trying to hide and show,
// i couldn't fllow where vaiable `name` came from, but this is
// how you could easily show or hide somthing based on this select's value
var $value = $(this).val();
// although, if you're gonna have alot of possible ifs,
// i would suggest a switch statement
switch($value) {
case "United Kingdom":
// here you can choose to show a specific select for UK states/countries/ares (i'm not familiar with how it works, i'm sorry)
$("#UKStates").show();
// if you're going to show specific, prefilled select boxes,
// then it would be advised to include a classname on each one
// that you can refer to in order to hide the rest
$(".select-states").hide();
break;
case "United States":
// do work
break;
default:
// do work
}
// otherwise, if you won't have many if statements,
// just use a regular if statment, like so:
// if ($value == "United Kindom") {
// $(".county").show();
// }
// else {
// $(".county").hide();
// }
})
});
notice in my example, there is alot of commented teaching, but not alota of code. Always remember, jQuery does all the work, just do what you need too.
You could start of by reducing your function to just this, making use of the showOrHide overload of .toggle().
function remove() {
$("select[name='ab']").toggle($("select[name='ab']").val() === "United Kingdom");
}
Since you are loading jQuery, you could as well make use of its event-handling as well, instead of using inline onchange="". Something like this:
$(function(){ // Wait for DOM to be ready
$("select[name='ab']").change(remove); // Attach an change-lister
});
So the complete thing would be:
$(function(){
$("select[name='ab']").change(remove);
});
function remove() {
$("select[name='ab']").toggle($("select[name='ab']").val() === "United Kingdom"));
}

edit in place with jQuery selecting newly inserted elements

I am trying to write my own edit-in-place using jQuery. My code is something like this;
$(".comment-edit").bind({
click: function(){
commentId = $(this).parent().parent().attr("id");
commentEditable = $("#"+commentId+" .comment-text");
if (commentEditable.hasClass('active-inline')) {
alert("already editable aq");
return false;
}
contents = $.trim($("#"+commentId+" .comment-text").text());
commentEditable.addClass("active-inline").empty();
var editBox = '<textarea id="newComment"cols="50" rows="6"></textarea><button class="newCommentSave">Save</button><button class="newCommentCancel">Cansel</button>';
$(editBox+" textarea").val(contents).appendTo(commentEditable).focus();
$.(".newCommentSave").live({
click: function(){
alert("Save");
return false;
}
});
$.(".newCommentCancel").click(function(){
alert("Cancel");
return false;
});
return false;
}
});
As you can see I tried both "live()" and "click()" for interacting with the newly created buttons. However this doesn't work.
I am getting XML filter is applied to non-XML value (function (a, b) {return new (c.fn.init)(a, b);})
Any ideas? What seems to be going wrong?
Edit:
Html looks something like this:
<div class="comment" id="comment-48">
<div class="comment-author">
defiant
<span class="date">2010-11-09 01:51:09</span>
</div>
<div class="comment-text">Comment Text....</div>
</div>
The problem is here:
var editBox = '<textarea id="newComment"cols="50" rows="6"></textarea><button class="newCommentSave">Save</button><button class="newCommentCancel">Cancel</button>';
$(editBox+" textarea").val(contents).appendTo(commentEditable).focus();
editBox is a string, so you're getting this as a result:
$("<textarea/><button /> textarea")
...which isn't XML or a valid selector, throwing an error. Instead you want this:
$(editBox).filter("textarea").val(contents)
.end().appendTo(commentEditable).focus();
This gets the <textarea> from that object you just created via .filter() (since it's a root level element), sets the contents, then uses .end() to hop back in the chain to $(editBox) which contains both elements to append. This would focus the button though, so you may want this instead:
$(editBox).appendTo(commentEditable).filter("textarea").val(contents).focus();
As it turns out, the reason for XML error was a "."
$.(".newCommentSave").live({
// stuff
})
The dot after the dollar sign is what causing this error. At least the code was working fine without it.
I tend to do something like this to attach a click event (to a span in my example)
var span = $("<span>some text</span>");
span.click( function() { alert('yay'); });
I'd break down your editBox variable into three different variables and see what happens then.
The .live() syntax is .live('event', function), I don't think it accepts a map of event:function pairs.
So would something like this work?
$.(".newCommentSave").live('click', function(){
alert("Save");
return false;
});
I'm not sure why your .click() handler didn't work though.

Javascript Bind on Blur, Both 'if indexOf' and 'else' Performed

HTML
<!-- Contents of div #1 -->
<form id="6hgj3y537y2biacb">
<label for="product_calendar" class="entry_label">Calendar</label>
<input type="text" name="product_calendar" class="entry" value="" />
</form>
<form id="pyc2w1fs47mbojez">
<label for="product_calendar" class="entry_label">Calendar</label>
<input type="text" name="product_calendar" class="entry" value="" />
</form>
<form id="kcmyeng53wvv29pa">
<label for="product_calendar" class="entry_label">Calendar</label>
<input type="text" name="product_calendar" class="entry" value="" />
</form>
<!-- Contents of div #2 -->
<div id="calendar_addRemove"> <!-- CSS >> display: none; -->
<div id="calendar_add">
<label for="calendar_add" class="calendar_addLabel">Add Occurrence</label>
<input type="text" name="calendar_add" class="calendar_addInput" value=""/>
</div>
<div id="calendar_remove">
<label for="calendar_remove" class="calendar_removeLabel">Remove Occurrence</label>
<input type="text" name="calendar_remove" class="calendar_removeInput" value=""/>
</div>
</div>
Javascript
// Complete behavioral script
$(function() {
$('input[name=product_calendar]').css({ 'color': '#5fd27d', 'cursor': 'pointer' }).attr({ 'readonly': 'readonly' }); // Additional formatting for specified fields
$('input[name=product_calendar]').focus(function() { // Focus on any 'input[name=product_calendar]' executes function
var product_calendar = $(this); // Explicit declaration
var attr_val = $(product_calendar).attr('value');
$('#calendar_addRemove input').attr({ 'value': '' }); // Clear input fields
$('#calendar_addRemove').fadeIn(500, function() { // Display input fields
$('input[name=calendar_add]').blur(function() { // After value entered, action occurs on blur
alert('Blur'); // Added for testing
var add_val = $('input[name=calendar_add]').attr('value');
if (add_val != '') {
alert('Not Blank'); // Added for testing
var newAdd_val = attr_val + ' ' + add_val;
$(product_calendar).attr({ 'value': newAdd_val });
$('#calendar_addRemove').fadeOut(500);
}
else {
alert('Blank'); // Added for testing
$('#calendar_addRemove').fadeOut(500);
}
});
$('input[name=calendar_remove]').blur(function() { // After value entered, action occurs on blur
alert('Blur'); // Added for testing
var remove_val = $(this).attr('value');
if (remove_val != '') {
alert('Not Blank'); // Added for testing
if (attr_val.indexOf(remove_val) != -1) {
alert('Eval True'); // Added for testing
var newRemove_val = attr_val.replace(remove_val, '');
$(product_calendar).attr({ 'value': newRemove_val });
$('#calendar_addRemove').fadeOut(500);
}
else {
alert('Eval False'); // Added for testing
$('#calendar_remove').append('<p class="error">Occurrence Not Found</p>');
$('.error').fadeOut(1500, function() { $(this).remove(); });
}
}
else {
alert('Blank'); // Added for testing
$('#calendar_addRemove').fadeOut(500);
}
});
});
});
});
I've added a few alerts to see the order this script is performing in. When I enter 1234 into input[name=calendar_add] and blur, the alerts come up as expected. Then, when I proceed and enter 1234 into input[name=calendar_remove] and blur, this script throws up alerts in the following order: Blur, Not Blank, Eval False, Blur, Not Blank, Eval True - If I repeat this process, the occurrence of my alerts double every time (both add and remove), however keeping the same order (as if in sets).
I think the issue is multiple value re-declaration of the variable attr_val in the DOM, but I'm not quite sure how to revise my script to alleviate this issue.
It doesn't. That is not possible.
So, there are some possible reasons that it might seem so:
The code that actually runs doesn't look like that. It might be an older version that is cached, or you are looking in the wrong file.
The code runs more than once, that way both execution branches may run. (Although I can't really see any possibility for that here.)
You are misinterpreting the result, and whatever you see that leads to the conclusion that both branches have to be executed, is in fact caused by some other code.
You could use a debugger to set breakpoints in the code. Set one breakpoint before the condition, and one in each branch. Then you will see if the code runs twice, once or not at all.
Edit:
The alerts that you added to the code shows that the event is actually called twice, and the first time the values are not what you think that they are.
Add some code to try to find out where the event is invoked from. Catch the event object by adding it to the function signature: .blur(function(e) {. Then you can use e.currentTarget to get the element that triggered the event, and display some attributes from it (like it's id) to identify it.
Edit 2:
I am curios about this line:
$(product_calendar).attr({ value: newRemove_val });
Do you create the variable product_calendar somewhere, or did you mean:
$('input[name=product_calendar}').attr({ value: newRemove_val });
Edit 3:
Seeing the complete code, the cause of the double execution is clear. You are adding even handlers inside an event handler, which means that another handler is added every time.
The reason for attr_val not working properly is because it's created as a local variable in one function, and then unsed in another function.
Add the blur handlers from start instead, and they occur only once. Declare the variable outside the function.
Some notes:
You can use the val function instead of accessing the value attribute using the attr function.
When you assign $(this) to product_calendar, it's a jQuery object. You don't have to use $(product_calendar).
The removal doesn't match complete values, so you can add 12 and 2, then remove 2 and you get 1 and 2 left.
(this is a dummy text, because you can't have a code block following a list...)
// Complete behavioral script
$(function() {
// declare variables in outer scope
var attr_val;
var product_calendar;
$('input[name=product_calendar]')
.css({ 'color': '#5fd27d', 'cursor': 'pointer' })
.attr('readonly', 'readonly') // Additional formatting for specified fields
.focus(function() { // Focus on any 'input[name=product_calendar]' executes function
product_calendar = $(this); // Explicit declaration
attr_val = product_calendar.val();
$('#calendar_addRemove input').val(''); // Clear input fields
$('#calendar_addRemove').fadeIn(500); // Display input fields
});
$('input[name=calendar_add]').blur(function() { // After value entered, action occurs on blur
var add_val = $(this).val();
if (add_val != '') {
product_calendar.val(attr_val + ' ' + add_val);
}
$('#calendar_addRemove').fadeOut(500);
});
$('input[name=calendar_remove]').blur(function() { // After value entered, action occurs on blur
var remove_val = $(this).val();
if (remove_val != '') {
if (attr_val.indexOf(remove_val) != -1) {
product_calendar.val(attr_val.replace(remove_val, ''));
$('#calendar_addRemove').fadeOut(500);
} else {
$('#calendar_remove').append('<p class="error">Occurrence Not Found</p>');
$('.error').fadeOut(1500, function() { $(this).remove(); });
}
} else {
$('#calendar_addRemove').fadeOut(500);
}
});
});
OK, I think I understand the issue now.
Every time you do a focus on the product_calendar elements, you do a fadeIn on the #calendar_addRemove element. Every time you do that fadeIn, you use its callback to bind new blur handlers to the calendar_add and calendar_remove elements. That means that over time, those elements will have multiple blur handlers (all executing the same logic.) That can't be what you want.
In the script below, I've pulled out the nested handlers so that they're only bound once to each element. Note that:
product_calendar is declared (as null) at the top of the anonymous function, and then updated by the focus handler on the product_calendar element. I think this results in correct behavior.
attr_val is declared and assigned locally in both of the blur handlers. Again, I think this results in the correct behavior: If you were to declare it outside of the blur handlers (as product_calendar is declared), then you might accidentally use old values when you access it.
I'm still not sure exactly how this code is supposed to function, but this script performs in a way that I'd consider "reasonable".
(By the way, production code should probably allow for whitespace at the beginning and end of the input strings.)
$(function() {
var product_calendar = null;
$('input[name=product_calendar]').css({ 'color': '#5fd27d', 'cursor': 'pointer' }).attr({ 'readonly': 'readonly' }); // Additional formatting for specified fields
$('input[name=calendar_add]').blur(function() { // After value entered, action occurs on blur
alert('Blur'); // Added for testing
var add_val = $('input[name=calendar_add]').attr('value');
if (add_val != '') {
alert('Not Blank'); // Added for testing
var attr_val = $(product_calendar).attr('value');
var newAdd_val = attr_val + ' ' + add_val;
$(product_calendar).attr({ 'value': newAdd_val });
$('#calendar_addRemove').fadeOut(500);
}
else {
alert('Blank'); // Added for testing
$('#calendar_addRemove').fadeOut(500);
}
});
$('input[name=calendar_remove]').blur(function() { // After value entered, action occurs on blur
alert('Blur'); // Added for testing
var remove_val = $(this).attr('value');
if (remove_val != '') {
alert('Not Blank'); // Added for testing
var attr_val = $(product_calendar).attr('value');
if (attr_val.indexOf(remove_val) != -1) {
alert('Eval True'); // Added for testing
var newRemove_val = attr_val.replace(remove_val, '');
$(product_calendar).attr({ 'value': newRemove_val });
$('#calendar_addRemove').fadeOut(500);
}
else {
alert('Eval False'); // Added for testing
$('#calendar_remove').after('<p class="error">Occurrence Not Found</p>');
$('.error').fadeOut(1500, function() { $(this).remove(); });
}
}
else {
alert('Blank'); // Added for testing
$('#calendar_addRemove').fadeOut(500);
}
});
$('input[name=product_calendar]').focus(function() { // Focus on any 'input[name=product_calendar]' executes function
product_calendar = $(this);
$('#calendar_addRemove input').attr({ 'value': '' }); // Clear input fields
$('#calendar_addRemove').fadeIn(500);
});
});

Categories

Resources