Finding child of previous sibling in jQuery - javascript

I am modifying existing code, so I can only affect the elements that exist rather an building it right. I am iterating over all spans that have the class reqd, and if so, I'm either taking the immediate previous sibling (example 1) or the previous sibling's first child of type input (example 2) and appending jQuery validate attributes to it.
Using prev() was for example #1, which was easy. I have to use prevUntil() for examples 2 and 3 to isolate better where I am targeting the DOM.
My issue is with example #2. While I can reach class="regfieldlabel I cannot get it to traverse its children. Example #3 is like #2 except with an empty span tag in the middle.
//Example code 1
<input type="text" name="str_cvv" value="" maxlength="3" size="4" id="CVV">
<span class="reqd">*</span>
//Example code 2
<span class="regfieldlabel">
<input type="text" name="nameOnCard" size="30" maxlength="50" id="cardName" value="">
</span>
<span class="reqd">*</span>
//Example code 3
<span class="regfieldlabel">
<input type="text" name="nameOnCard" size="30" maxlength="50" id="cardName" value="">
</span>
<span class="regfieldlabel"> </span>
<span class="reqd">*</span>
<script>
$(function() {
$("span").each(function(){
//Highlight all fields that have a red asterisk at the end for validation
if ($(this).hasClass('reqd')) {
$(this).prev().attr('data-rule-required', true).attr('data-msg-required', 'This field is required');
$(this).prevUntil('.regfieldlabel').find('input').attr('data-rule-required', true).attr('data-msg-required', 'This field is required');
}
});
});
</script>

You don't want to use prevUntil, from the docs you will see:
Description: Get all preceding siblings of each element up to but not
including the element matched by the selector, DOM node, or jQuery
object.
As a result, $(this).prevUntil('.regfieldlabel') is actually empty.
For Example 2, you still want to use just prev:
$(this).prev('.regfieldlabel').find('input')
For a general solution, you will have to check which situation you are in, in order to do the correct thing:
$("span.reqd").each(function(){
var $this = $(this);
var $prev = $this.prev();
var $input = $([]); // init to empty
if($prev.is("input")){
// EXAMPLE 1
$input = $prev;
} else if($prev.is(".regfieldlabel")){
var $innerInput = $prev.find("input");
if($innerInput.length > 0){
// EXAMPLE 2
$input = $innerInput;
} else {
$prev = $prev.prev(".regfieldlabel");
$innerInput = $prev.find("input");
if($innerInput.length > 0){
// EXAMPLE 3
$input = $innerInput;
} else {
// unknown case, maybe do something here
}
}
} else {
// unknown case, maybe do something here
}
$input.attr('data-rule-required', true).attr('data-msg-required', 'This field is required');
});
http://jsfiddle.net/DVSeg/2/

Related

Setting values of dynamically added input fields changing all input fields

I have a form where users can create recipes. I start them off with one ingredient field (among others) and then use .append() to add as many more as they want to the div container that holds the first ingredient. The first input field has an id of IngredientName1 and dynamically added input fields are IngredientName2, IngredientName3, etc.
When they start typing in the input field, I pop a list of available ingredients filtered by the value they key into IngredientNameX. When they click on an ingredient in the list, it sets the value of the IngredientNameX field to the text from the div - like a search & click to complete thing. This all works very well; however, when you add IngredientName2 (or any beyond the one I started them with initially) clicking on an available ingredient sets the values of every single IngredientNameX field. No matter how many there are.
I hope this is enough context without being overly verbose, here's my code (I've removed a lot that is not relevant for the purpose of posting, hoping I didn't remove too much):
<div id="ingredientsContainer">
<input type="hidden" id="ingredientCounter" value="1">
<div class="ingredientsRowContainer">
<div class="ingredientsInputContainer"><input class="effect-1 ingredientsInput" type="text" name="IngredientName1" placeholder="Ingredient" id="IngredientName1" data-ingID="1"><span class="focus-border"></span></div>
</div>
<input type="hidden" name="Ingredient1ID" id="Ingredient1ID">
</div>
<script>
$(document).ready(function(){
$(document).on('keyup', "[id^=IngredientName]",function () {
var value = $(this).val().toLowerCase();
var searchValue = $(this).val();
var valueLength = value.length;
if(valueLength>1){
var theIngredient = $(this).attr("data-ingID");
$("#Ingredients").removeClass("hidden")
var $results = $('#Ingredients').children().filter(function() {
return $(this).text() === searchValue;
});
//user selected an ingredient from the list
$(".ingredientsValues").click(function(){
console.log("theIngredient: "+theIngredient);//LOGS THE CORRECT NUMBER
var selectedIngredientID = $(this).attr("id");
var selectedIngredientText = $(this).text();
$("#IngredientName"+String(theIngredient)).val(selectedIngredientText);//THIS IS WHAT SETS EVERYTHING WITH AN ID OF IngredientNameX
$("#Ingredient"+String(theIngredient)+"ID").val(selectedIngredientID);
$("#Ingredients").addClass("hidden");
});
$("#Ingredients *").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
} else {
$("#Ingredients").addClass("hidden")
}
});
$("#AddIngredient").click(function(){
var ingredientCounter = $("#ingredientCounter").val();
ingredientCounter++;
$("#ingredientCounter").val(ingredientCounter);
$('#ingredientsContainer').append('\
<div class="ingredientsRowContainer">\
<div class="ingredientsInputContainer"><input class="effect-1 ingredientsInput" type="text" name="IngredientName'+ingredientCounter+'" placeholder="Ingredient" id="IngredientName'+ingredientCounter+'" data-ingID="'+ingredientCounter+'"><span class="focus-border"></span></div>\
</div>\
<input type="hidden" name="Ingredient'+ingredientCounter+'ID" id="Ingredient'+ingredientCounter+'ID">\
');
});
});
</script>
[UPDATE] I realized the problem is happening because the function is running multiple times. I assume this happening because I'm calling a function on keyup of a field whose id starts with IngredientName so when one has a key up event, all existing fields run the function. How do i modify my:
$(document).on('keyup', "[id^=IngredientName]",function () {
to only run on the field with focus?

display a hidden input field when enter value on a particuloar input filed

anyone could help me out on how i could achieve this with either javascript or jquery maybe to get the following as mentioned below
say i have this field1
<input type="text" name="field1" value="">
and then i have this field2
<input type="hidden" name="field2" value="">
what i mean to say the field2 should be hidden but if someone enters some value in field1 then field2 shows but if no value on field1 then it disappears?
thanks in advance and appreciate your time and help
You'd get the first field, check if it has a value, and toggle the second field based on that, but you should not be using a hidden input, but instead hide it with CSS
$('[name="field1"]').on('input', function() {
var el = $('[name="field2"]').toggle( this.value !== "" );
if (this.value === "") el.val("");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" name="field1" value="" placeholder="type something">
<br><br>
<input type="text" name="field2" value="" style="display:none">
As you've also tagged your question with JavaScript it seems worth offering the following:
// retrieving the first - if any - element with its
// 'name' attribute equal to the value of 'field1':
var input = document.querySelector('[name=field1]');
// adding an event-listener to that element, listening
// for the 'input' event (keyup, paste, copy...) and
// assigning the method's anonymous function as the
// event-handler:
input.addEventListener('input', function(e) {
// 'e': here unused, is a reference to the event
// which triggered the function to be called; using
// e.type will give the specific event, if required
// (and other properties are, of course, available).
// retrieving the first - if any - element with has
// its 'name' attribute equal to 'field2':
var conditionalInput = document.querySelector('[name=field2]');
// if the value of the <input> element that received
// the event has a value that, when leading and trailing
// white-space is removed, results in a truthy
// evaluation (the string length is non-zero):
if (this.value.trim().length) {
// we set the display style of the conditionally-
// shown <input> to 'block', you could instead use
// 'inline-block' if you prefer:
conditionalInput.style.display = 'block';
// otherwise, if the length of the trimmed-value is
// zero (falsey):
} else {
// we set the display style of the conditionally-
// shown <input> to 'none':
conditionalInput.style.display = 'none';
// and also remove its entered value:
conditionalInput.value = '';
}
});
var input = document.querySelector('[name=field1]');
input.addEventListener('input', function(e) {
var conditionalInput = document.querySelector('[name=field2]');
if (this.value.trim().length) {
conditionalInput.style.display = 'block';
} else {
conditionalInput.style.display = 'none';
conditionalInput.value = '';
}
});
<input type="text" name="field1" value="" />
<input type="text" name="field2" value="" />
In your HTML please note that I've adjusted the <input> element's type, from 'hidden' to 'text', this is because some browsers – I believe mostly Internet Explorer – has, or had, issues when changing the type of an <input> element dynamically.
If your use-case doesn't depend on cross-browser compatibility then you can, of course, change the type (conditionalInput.type = 'text'/conditionalInput.type = 'hidden') rather than the display.

Check if all input/select fields of a table row have values

There is a table with some input and select fields in a row. I want to check if all input and select fields of an row have a value. This is how I would think to do that, but do I have to use closest and find? I think this is not optimal.
HTML
<table>
<tr>
<td><select><option></option><option>Select anything</option></td>
<td><input type="text" name="field1"></td>
<td><input type="text" name="field2"></td>
</tr>
<tr>
<td><select><option></option><option>Select something</option></td>
<td><input type="text" name="field3"></td>
<td><input type="text" name="field4"></td>
</tr>
</table>
JS
'change #table input, change #table select': function(event) {
var $this = $(event.currentTarget),
$row = $this.closest('tr'),
$elements = $row.find('input, select');
var empty = false;
$elements.each(function(index) {
if (!$(this).val()) empty = true;
});
if (empty)
console.log('some value is missing')
else {
console.log('valide');
// do something with values
}
}
There are really two questions here:
Most optimal method to select all inputs in a table row
Ensure all the inputs have a value
For the first question there is a subliminal side to that. Ensure that it IS an input and then select it within the context of the current row of the changed input.
First off, jQuery uses the Sizzle (https://sizzlejs.com/) engine under the covers for selection. One thing to be aware of is the "right to left" processing of the selector string by that engine.
Thus the most optimal selection is somewhat browser specific but the fastest way to select is an ID followed in modern browsers by a class. Some older browsers do not select by class as well but let's leave that for your research.
Selection: Bad way to do stuff
So given that, let's look at a complex selector that you might use:
'div.mycontainer div.mytablecontainer>table#mytable.mytableclass tr td select, div.mycontainer div.mytablecontainer>table#mytable.mytableclass tr td input'
First off DO NOT USE THAT. Now to explore why not: Remember we talked about the "right to left" selector processing? For discussion let us narrow down out selector to the last part:
"div.mycontainer div.mytablecontainer>table#mytable.mytableclass tr td input"
What this does then in starting on the right:
"find all the inputs in the DOM",
use that list of those inputs, "find all the inputs in a td element
use those td elements, find all those in a tr
find all those tr in a .mytableclass element
find all those in an element with an id of mytable (remember this ID MUST be unique)
Now keep going, find that single element id that is a table element
That is an immediate child of an element with classmytablecontainer
That is a DIV element div
That is a child of an element with class mycontainer
That is a DIV element div
Whew that's a lot of work there. BUT we are NOT DONE! We have to do the same thing for the OTHER selector in there.
Selection: Better way to do stuff
NOW let's do this better; first off let's leverage the modern browser class selector by adding a class to all our "scoped" inputs - things we want to check for entry.
<input class="myinput" />
It does really need a type="" attribute but ignore that for now. Let's use that.
$('#mytable').find('.myinput');
What this does is:
Select the element with ID of 'mytable' which is the FASTEST selector in all browsers; we have already eliminated those 47 other tables in our DOM.
Find all the elements with a class of class="myinput"; within that table; in modern browsers this is also very fast
DONE. WOW! that was SO much less work.
Side note on the .find() instead of "#mytable input"
Remember our right to left once again? Find all inputs in the DOM, then narrow to those inputs we found that are in that table NO STOP THAT right now.
Or (better likely) "#mytable .myinput"
SO our "rules" of selecting a group of elements are:
Use an ID to limit scope to some container if at all possible
Use the ID by itself NOT part of a more complex selector
FIND elements within that limited scope (by class if we can)
Use classes as modern browsers have great selection optimization on that.
When you start to put a space " " or ">" in a selector be smart, would a .find() or .children() be better? In a small DOM perhaps maintenance might be easier, but also which is easier to understand in 4 years?
Second question: not specific but still there
You cannot simply globally use !$(this).val() for inputs.
For a check box that is invalid. What about radio buttons? What about that <input type="button" > someone adds to the row later? UGH.
SO simply add a class to all "inputs" you DO wish to validate and select by those:
<input type="text" class="validateMe" />
<select class="validateMe" >...
Side note you MIGHT want to sniff the TYPE of the input and validate based upon that: How to get input type using jquery?
EDIT: Keep in mind your validation input MIGHT have a "true/false" value so then this might fail: !$(this).val() (radio buttons, checkbox come to mind here)
Some code and markup:
<table id="mytable">
<tr>
<td>
<select class="myinput">
<option></option>
<option>Select anything</option>
</select>
</td>
<td>
<input class="myinput" type="text" name="field1" />
</td>
<td>
<input class="myinput" type="text" name="field2" />
</td>
</tr>
<tr>
<td>
<select class="myinput">
<option></option>
<option>Select something</option>
</select>
</td>
<td>
<input class="myinput" type="text" name="field3" />
</td>
<td>
<input class="myinput" type="text" name="field4" />
</td>
</tr>
</table>
<div id="results">
</div>
probably NOT want a global (namespace the "selectors")
var selectors = '.myinput';
$('#mytable').on('change', selectors, function(event) {
var $this = $(event.currentTarget),
$row = $this.closest('tr'),
$elements = $row.find(selectors);
var $filledElements = $elements.filter(function(index) {
return $(this).val() || this.checked;
});
var hasEmpty = $filledElements.length !== $elements.length
var rowIndex = $row.index();
$('#results').append("Row:" + rowIndex + " has " + $filledElements.length + ' of ' + $elements.length + ' and shows ' + hasEmpty + '<br />');
if (hasEmpty)
console.log('some value is missing');
else {
console.log('valide');
// do something with values
}
});
AND something to play with: https://jsfiddle.net/MarkSchultheiss/fqadx7c0/
If you're only selecting on particular element with knowing which parent to select with, you should try using .filter() to filter out only element that did't have a value like following :
$('button').click(function() {
var h = $('table :input').filter(function() {
return $(this).val() == "" && $(this);
}).length;
alert(h);
});
DEMO
I did this plunk
https://plnkr.co/edit/q3iXSbvVWEQdLSR57nEi
$(document).ready(function() {
$('button').click(function() {
var table = $('table');
var rows = table.find('tr');
var error = 0;
for (i = 0; i < rows.length; i++) {
var cell = rows.eq(i).find('td');
for (a = 0; a < cell.length; a++) {
var input = cell.eq(a).find(':input');
if (input.val() === "") {
input.css("border", "solid 1px red");
error++;
} else {
input.css("border", "solid 1px rgb(169, 169, 169)");
}
}
}
if (error > 0){
alert('Errors in the form!')
return false;
} else {
alert('Form Ok!')
return true;
}
})
})
Simple Jquery validation, searching all the inputs (including selects), if it's null, increment the error counter and change class. If the error counter is > 0, alert error and return false;
Maybe isn't the best solution, but it sure can help get started.

Changing to an Error Class

I need to validate the length of a string in the inputbox. If the string is invalid it will trigger an onblur event. When this event is triggered, it must turn the label of the object associated with the trigger red. However, there is a css class that exist that contains the style, so I just need to change the class to that class.
The prefix is some value being passed in.
Here is sample code.
var input = $(this).val();
var findLabel = document.getElementById`enter code here`(prefix+'findLabel');
if(input.length < min || input.length > max){
//alert is to check the values for testing
alert('Value for selected country '+selectedCountry+' must be between '+min+' and '+max+' characters');
}
example
<div class="Value_s123" id="a1Values123" style="float:left;">
<span class="avalue" id="a1value">
<label class="classOff" id="a1ValueLabel">some value</label>
<span style="white-space: nowrap;">
<input id="a1CatchValue" maxlength="15" name="a1Value" onblur="requiredFieldValidation("a1Postal")" size="15" tabindex="13" type="text"><img alt="Required" height="9" id="a1ValueReqdImg" src="/img/icn_dia.gif" width="11">
</span>
</span>
</div>
I need to get the a1ValueLabel and the class classOff and turn it to classON
Use this:
$("#a1ValueLabel").removeClass("classOff").addClass("classOn");
Here's what I came up with. The script is run every time someone types, once it reaches more than 5 characters the script adds the class "classOn" to the label tag. If there are 5 or less characters, it removes the "classOn" and adds the "classOff" class.
This is not a sustainable way to do it however, if you wanted to run this script on multiple items.
$(document).ready(function(){
$('#a1CatchValue').on('keyup',function(){
if ($(this).val().length > 5) {
$('#a1ValueLabel').removeClass('classOff');
$('#a1ValueLabel').addClass('classOn');
}
else {
$('#a1ValueLabel').removeClass('classOn');
$('#a1ValueLabel').addClass('classOff');
}
});
});

jscript 'type' attribute

Im trying to implement the following logic on javascript.
If type is 'bankAccountTypeId' get all fields with the same className as field using $(field.className) then use .each to loop through each result compare field.value with $(this).val() and use alert to show an error message if they are different (break if fail).
function onChange_productListField(field, type) {
if (HimmsJSUtil.elementHasClass(field, 'DC')) {
var allProductGroupFields = $(".DC."+type);
var value = field.value;
if (field.options) {
value = HimmsJSUtil.getSelectedDropDownOption(field);
}
allProductGroupFields.each(function(index) {
if ($(this).attr("id") != field.id
&& !$(this).val()) {
$(this).val(value);
}
});
} else {
/* implement the logic here */
}
}
My question is , how would the type attribute work, within this logic?
Firsly let's make clear that jscript is not javascript and "type" in your code is not attribute but just a parameter of the function.
var allProductGroupFields = $(".DC."+type);
The above line uses jQuery to select a group of elements having classes "DC" and "bankAccountTypeId" at the same time where type is "bankAccountTypeId". Such a code can be used in a structure like this:
<div class="whatever">
<input type="text" class="DC bankAccountTypeId" />
<input type="text" class="DC userId" />
<a href="http://stackoverflow.com/questions/1041344">
jQuery multiple class selector
<a>
</div>
Extra:
For a structure like this
<div class="DC">
<input type="text" class="bankAccountTypeId" />
<input type="text" class="userId" />
<a href="http://stackoverflow.com/questions/3767512">
jQuery class within class selector
<a>
</div>
the selector line must be changed to
var allProductGroupFields = $(".DC ."+type);

Categories