I am dynamically converting a list of options to a series of radio buttons. One of the values that is in the option has a literal quote mark.
<option value="6\" tall">
When I loop through each option, pull the a value $(this).val(); then I build a string for the radio button:
rb="input id=\"CTRL"+fld+"_"+cnt+"\" name=\""+fld+"\" type=\"radio\" "+ sel+" value=\"" +val+ "\"" +valCaption";
If I debug and break on this, the string is created correctly, but when I insert the element, the literal in the value is lost and the result is:
<input id="CTRLText1_3" name="Text1" type="radio" value="Rev 6" tall"="">
I cannot escape the value because then the value doesn't actually match what is being returned by the original select list.
Any ideas how I can create this radio button?
In HTML \" doesn't mean literal quote, it just means backslash followed by a double-quote. In HTML to produce a quote character when you need to, you use HTML Entities and the specific entity for a double quote is: ".
<input value=""To be or not to be"">
\" is the escape code for a double quote when that string is parsed by the JavaScript runtime, not the HTML parser.
So, value="6\" doesn't contain a literal quote. It contains 6\. The quote after the \ is the closing quote for the value attribute which causes tall" to remain as invalid HTML that the HTML parser just ignores. When you pull the value of the input, you would only get 6\.
You stated that the string gets created correctly, but it doesn't. Look closely at the end of what you say you are getting;
<input id="CTRLText1_3" name="Text1" type="radio" value="Rev 6" tall"="">
tall"="" is invalid HTML.
To do what I think you are after, you should use single quotes for the HTML attribute and then the double quote can be properly stored for JavaScript to extract later. You won't need any escaping:
console.log(document.querySelector("option").value);
<option value='6" tall'>
More info:
I tried building the radio buttons with an empty "value" attribute:
<input type="radio" value="">
The option tag actually looks like this:
<option value="6\" tall">
As I loop through my options I put the values into an array. Then after I add the HTML for the radio buttons to the page, I loop through the array and add the value to the attribute of the radio button.
for(x=0;x<arrVals.length; x++){
$("#CTRL"+fld+"_"+x).attr("value",arrVals[x]);
}
The end result, though is this:
<input id="CTRLText1_1" name="Text1" type="radio" value="Rev 6" tall">
The = sign has been escaped with """ While this is closer, it unfortunately is not the same. The original value in the option tag does not match the end-result value of the radio button.
Quotes in HTML
There's two easy ways to avoid conflicting quotes:
Instead of using double quotes in the HTML, use single quotes.
<option value='6" tall'>6" tall</option>
<option> will default to its text content if value is not assigned.
<option>7" tall</option>
Strings
The use of complex literal strings with concatenated variables are a little more manageable if you alternate between double and single quotes:
// Literal string concatenation syntax: '+VAR+'
let literalString = '<input id="'+ID+'" type="radio">';
Template literals interpolate variables and expressions:
// Template literal interpolation syntax: ${VAR}
let templateLiteral = `<input id="${ID}" type="radio">`;
I prefer the use of template literals for longer complex strings and literal strings for short simple strings.
Demo
The following demo collects all values into an array then creates a simple radio button then adds the attributes and values to it using simple JavaScript property assignment and the array of values. The use of htmlStrings is very error prone consider the alternative presented in demo.
Note: details are commented in demo
// Define counter
let i = 0;
// Delegate click event to button.add
$('.add').on('click', function(e) {
// Increment counter
i++;
/*
.map() will collect all .data and return their values
.get() will place all of the values into an array
V is an array of values
*/
let V = $('.data').map(function() {
return $(this).val();
}).get();
// Checking V array
console.log(JSON.stringify(V));
// Append a radio button to fieldset.box
$('.box').append(`<input type='radio'>`);
/*
- Reference the last radio button
- Dereference the last radio button from a jQuery Object
to a JavaScript DOM Node by suffixing [0] to it.
- The reason for dereferencing is to be able to use
simple plain JavaScript properties
*/
const rad = $('.box :radio:last')[0];
/*
Values are referenced by index from the V array
The attributes are set by property assignments
*/
rad.id = 'CTRL' + V[0] + '_' + i;
rad.name = V[0];
rad.value = V[1] + ' ' + V[2];
// Checking the radio button HTML
console.log(rad.outerHTML);
});
/*
This function just demonstrates what each radio button's
value is when rendered as HTML
*/
$('#formA').on('change', function(e) {
if ($(e.target).is(':checked')) {
$('#view').html($(e.target).val());
}
});
:root,
body {
font: 400 3vw/1.45 Consolas
}
select,
input,
button {
display: inline-block;
font: inherit;
height: 3ex;
line-height: 1;
width: 10ch;
margin-bottom: 8px;
padding: 0 2px;
vertical-align: middle
}
select {
padding: 2px 2px 4px 2px;
width: 12ch;
}
button {
padding: 0px 3px 5px;
line-height: 1.25;
width: 6ch;
cursor: pointer
}
#view {
color: tomato
}
<form id='formA'>
<input id='fld' class='data' value='Text1'>
<input id='val' class='data' value='Rev'>
<select id='valCaption' class='data'>
<option value='6" tall'>6" tall</option>
<option>7" tall</option>
</select>
<button class='add' type='button'>ADD</button>
<fieldset class='box'>
<output id='view'></output><br>
</fieldset>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
Thank you for everyone's suggestions. I did find the problem. I actually had to first collect the values from the options, then build the radio buttons with a blank value attribute and add that to the page. Then loop back through and just the $(fieldid).val(value) to set the values.
The other issue with saving the value then putting our page back into edit mode and the value not being re-selected. The ultimate problem is a bug in the core of the software where its not correctly comparing the stored value to the value in the list.
Related
I have alot of data, that i can check, useing checkbox. when I 'Submit', and want to see all the data i checked, i get redirected to the result page. But lets say i choose to only check 1 element the URL is: http://XXX.xxx/Test/TestResults?ID%5B%5D=2728
this is okay, but lets say i choose to see 10 elements the URL is LONG:
http://XXX.xxx/Test/TestResults?ID%5B%5D=2728&ID%5B%5D=2726&ID%5B%5D=2727&ID%5B%5D=2725&ID%5B%5D=2724&ID%5B%5D=2723&ID%5B%5D=2722&ID%5B%5D=2721&ID%5B%5D=2720&ID%5B%5D=2719
and the problem is that if i choose to see 100 elements, the URL will become so long that the site can alert me with: 'Submitted URI too large!'
is there any way only to show the id number, and not the ID[] (ID%5B%5D)?
The ID[] is the ID%5B%5D part, and then the id number will be added to it.
echo "<input type = 'checkbox' name = 'ID[]' class = 'checkbox' value = '".$testjobids[$count]."'/>";
/*this is in another .php file where i get the ID*/
$SelectedTestjobids = $_GET['ID'];
Link is shorter
After useing _POST, i got this: http://XXX.xxx/Test/TestResults
Problem
The only problem i have now is that i can't link this site to anyone, because there isn't attached anything else but the site in the URL. Is there a way to make a uniqe number after the http://XXX.xxx/Test/TestResults, so it's possible to share the link and it stills shows the same data?
If you can't use a post request for some reason, you could copy the values you want and submit them as a more compact string, either manually or by adding the string to another form element before submitting.
Of course, your server code would have to know what to do with the comma-separated string when it receives it.
This example uses a hidden <textarea> element to hold the values:
// Identifies HTML elements
const
boxes = document.querySelectorAll(".box"),
area1 = document.getElementById("area1"),
btn1 = document.getElementById("btn1"),
div1 = document.getElementById("div1");
// Calls `submitCheckboxValuesAsText` when the button is clicked
btn1.addEventListener("click", submitCheckboxValuesAsText);
// Defines `submitCheckboxValuesAsText`
function submitCheckboxValuesAsText(){
// Puts checked values in the textarea
updateTextarea();
// Copies the text from the textarea
const text = area1.value;
// Clears the demo div
div1.innerHTML = "";
// Formats the copied text within the demo div
if(text){
div1.innerHTML = "Imagine this gets sent to your server:<br /><br />" +
"<span class='grey'>" + text + "</span>";
}
}
// Defines `updateTextarea`
function updateTextarea(){
area1.value = [...boxes] // `[...boxes]` converts `boxes` to a proper Array
.filter(box => box.checked) // Makes a new Array of just the checked boxes
.map(box => box.value) // Makes a new Array with the values of those boxes
.join(","); // Makes a string from the array, separated by commas
}
label{ margin-right: 15px; }
#btn1{ margin: 20px 5px; }
.grey{ background-color: #DDDDDD; padding: 2px 5px; }
<label><input type="checkbox" class="box" value="val1" /><span>box1</span></label>
<label><input type="checkbox" class="box" value="val2" /><span>box2</span></label>
<label><input type="checkbox" class="box" value="val3" /><span>box3</span></label>
<textarea id="area1" hidden></textarea>
<div><button id="btn1">Submit</button></div>
<div id="div1"></div>
(If you wanted to avoid submitting manually, you should probably populate the hidden field before the user clicks Submit, such as by using document.addEventListener("change", updateTextarea);.
You would also want to change the button to <input type="submit" />.)
I have some input field dynamically generated inside form. I am trying to read the value of hidden input and append to to the end of text area
.<input type="hidden" id="formtype_loans_0_comment" name="formtype[loans][0][comment]" disabled="disabled" value="VAlue 1 value 123" />
<textarea id="formtype_loans_0_description" name="formtype[loans][0][description]">Text Area 1 or 1 </textarea>
<input type="hidden" id="formtype_loans_1_comment" name="formtype[loans][1][comment]" disabled="disabled" value="VAlue value 123" />
<textarea id="formtype_loans_1_description" name="formtype[loans][1][description]">test desc</textarea>
and Here is the js code, but it's not working,
var values = [];
$("input[name='formtype[loans][][description]']").each(function() {
values.push($(this).val());
});
alert(values);
Your selector "input[name='formtype[loans][][description]']" won't match any elements, because the [] in the middle will not match to the [0] or [1] (etc.) in the middle of the actual element name attributes.
For the HTML shown you could use the attribute starts with selector [name^=value]:
$('input[name^="formtype[loans]"]').each(function() {
If each textarea will always immediately follow its associated hidden input then within the .each() loop that iterates over the inputs you can say $(this).next() to get the textarea.
If the textareas might be elsewhere in the DOM then you could find them by selecting by the name attribute based on the name of the current input:
$('textarea[name="' + this.name.replace("comment", "description") + '"')
Demonstrated in context:
$('input[name^="formtype[loans]"]').each(function() {
var val = this.value
// add input's value to end of associated textarea's existing value:
$('textarea[name="' + this.name.replace("comment", "description") + '"')
.val(function(i, v) { return v + ' ' + val })
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input type="hidden" id="formtype_loans_0_comment" name="formtype[loans][0][comment]" disabled="disabled" value="Hidden value 0" />
<textarea id="formtype_loans_0_description" name="formtype[loans][0][description]">Text Area A</textarea>
<input type="hidden" id="formtype_loans_1_comment" name="formtype[loans][1][comment]" disabled="disabled" value="Hidden value 1" />
<textarea id="formtype_loans_1_description" name="formtype[loans][1][description]">Text Area B</textarea>
If you want to simply replace the textarea's current value rather than adding to the end of it then you can simplify the above to:
$('input[name^="formtype[loans]"]').each(function() {
$('textarea[name="' + this.name.replace("comment", "description") + '"')
.val(this.value)
})
var values = [],
inputs = $('input[type="hidden"]'),
textareas = $('textarea');
if (inputs.length === textareas.length) {
$.each(inputs, function(i, input) {
var val = ($(input).val()) ? $(input).val(): undefined;
if (val) {
$(textareas).eq(i).empty().val(val);
}
});
}
alert(values);
The working code above assumes a couple of things:
There will always be one textarea per hidden input.
The associated textarea will always be the next sibling after the hidden input.
Even if that is not the case, there are still various ways to resolve this challenge. But I'll break down the different parts of the code:
First, instantiate your variables. Most importantly, cache your selected HTML elements into vars: touching the DOM is expensive and negatively impacts performance (e.g. querying the DOM each time in a loop).
Next, we put a conditional test to ensure there is one textarea for each input. No need to waste time iterating through a loop looking for elements that aren't there.
Finally, iterate through each of the selected inputs confirming each of them have a value. Again, no need manipulating textarea if there is no value to insert. If there is a value in the input, insert it into the textarea that occupies the same position as the input in each of your arrays of elements.
I have the following input tag in my dom and unable to add classes and/or ids:
<input type="radio" title=" ">
When I try
$('input[title=" "]')
or
$('input[title=" "]')
it does not return anything. Can I pull this based on that title?
Your selectors didn't work because you didn't use the proper symbol. The title attribute gets any html entities decoded, so is going to get decoded to the actual symbol (ascii code 160). So that means the text isn't going to match it, neither will a regular space (ascii code 32).
On a Windows OS you can type ALT+0160 to get the symbol. On other OS you will need to find the equivalent key combo, or copy paste from a character map.
console.log( "Number of inputs with regular space: ", $('input[title=" "]').length );
console.log( "Number of inputs with text: ", $('input[title=" "]').length );
console.log( "Number of inputs with symbol: ", $('input[title=" "]').length );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Regular space in title -->
<input type="checkbox" title=" ">
<!-- non-break space html entity -->
<input type="checkbox" title=" ">
<!-- encoded html entity which will match -->
<input type="checkbox" title=" ">
Would it suffice to select all non-blank title attributes?
$('input:not([title=""])').click(function(){
alert('non-blank title');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input type="text" title=" " />
This should work:
var isFound = $('[title=" "]').length === 1;
if (isFound) console.log('Element is found.');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div title=" "></div>
This subtle difference is to put an actual not a space character into selector.
It's a bit unwieldy can try a function to return the value of the title attribute and use that. If you are using this in a normal page, you can swap getTitle parameter below for a more specific selector like ID.
function getTitle(selector) {
return $(selector).attr('title');
}
$('input[title=' + getTitle('input[type="radio"]') + ']');
Use the following trick with JQuery's filter() function and RegExp.test function to get input tags with title attributes that contain :
var inputs = $("input[title]").filter(function(i){
return /\u00A0/.test($(this).attr('title'));
});
$("input[title*=' ']");
To get input elements with title attributes that contains a "regular" space use the following:
The [attribute*=value] selector matches every element whose attribute
value containing a specified value.
So basically here is my jsFiddle - http://jsfiddle.net/CmNFu/ .
And code also here -
HTML -
<b style="float: left; margin-right: 10px;">category 1</b><input type="checkbox" value="category1" style="float: left;" class="portfolio-category" /><br />
<b style="float: left; margin-right: 10px;">category 2</b><input type="checkbox" value="category2" style="float: left;" class="portfolio-category" /><br />
<br />
<br />
<input type="text" name="categories" id="portfolio-categories" />
jQuery -
jQuery(document).ready(function() {
jQuery(".portfolio-category").click(function() {
if(jQuery(this).is(":checked")) {
jQuery("#portfolio-categories").val(jQuery("#portfolio-categories").val()+" "+jQuery(this).val());
}
else {
var portfolioCategories = jQuery("#portfolio-categories").val();
alert("before + "+portfolioCategories);
var currentElement = jQuery(this).val()+" ";
alert(currentElement);
portfolioCategories = portfolioCategories.replace(currentElement, "");
alert(portfolioCategories);
}
});
});
Well basically what I would like to achieve is, when user checks the checkbox, the value automatically adds inside input field (Done, it's working, whooray!), but the problem is when it unchecks the checkbox, the value should be removed from input box (the problem starts here), it doesn't remove anything. You can see I tried assigning val() function to variables, but also without success. Check my example on jsFiddle to see it live.
Any suggestions? I guess replace() is not working for val(), is it?
So, is there any other suggestions?
I'd do this:
jQuery(document).ready(function() {
jQuery(".portfolio-category").on('change', function() {
var string = "";
$('input[type="checkbox"]').each(function() {
var space = string.length>0?' ':'';
string += this.checked?space+this.value:'';
});
$("#portfolio-categories").val(string);
});
});
FIDDLE
You have quite the issue with spaces in that input box. but we'll get to that in a moment.
first, this will kind of work (if it weren't for the spaces problem):
add this line before the last alert:
jQuery("#portfolio-categories").val(portfolioCategories);
this will work, but not always, as the last element you append doesn't have a space after it.
but if you change the 4th line to this:
jQuery("#portfolio-categories").val(jQuery("#portfolio-categories").val()+jQuery(this).val()+" ");
it will work, as it adds the space after each element, instead of before.
http://jsfiddle.net/CmNFu/5/
your issue was that you changed the values in the variable: portfolioCategories, but you haven't updated the input itself. (notice, changing the value of a string, doesn't change the value of the input it originally came from)
What you need is to insert back the string portfolioCategories into the input. Also the spaces are creating a lot of problems. You could use $.trim(str) to remove any leading and trailing spaces from a string.
Have updated your fiddle with a solution that works.
http://jsfiddle.net/CmNFu/11/
Hope this helps.
I am using ASP.Net MVC along with Jquery to create a page which contains a contact details section which will allow the user to enter different contact details:
<div id='ContactDetails'>
<div class='ContactDetailsEntry'>
<select id="venue_ContactLink_ContactDatas[0]_Type" name="venue.ContactLink.ContactDatas[0].Type">
<option>Email</option>
<option>Phone</option>
<option>Fax</option>
</select>
<input id="venue_ContactLink_ContactDatas[0]_Data" name="venue.ContactLink.ContactDatas[0].Data" type="text" value="" />
</div>
</div>
<p>
<input type="submit" name="SubmitButton" value="AddContact" id='addContact' />
</p>
Pressing the button is supposed to add a templated version of the ContactDetailsEntry classed div to the page. However I also need to ensure that the index of each id is incremented.
I have managed to do this with the following function which is triggered on the click of the button:
function addContactDetails() {
var len = $('#ContactDetails').length;
var content = "<div class='ContactDetailsEntry'>";
content += "<select id='venue_ContactLink_ContactDatas[" + len + "]_Type' name='venue.ContactLink.ContactDatas[" + len + "].Type'><option>Email</option>";
content += "<option>Phone</option>";
content += "<option>Fax</option>";
content += "</select>";
content += "<input id='venue_ContactLink_ContactDatas[" + len + "]_Data' name='venue.ContactLink.ContactDatas[" + len + "].Data' type='text' value='' />";
content += "</div>";
$('#ContactDetails').append(content);
}
This works fine, however if I change the html, I need to change it in two places.
I have considered using clone() to do this but have three problems:
EDIT: I have found answers to questions as shown below:
(is a general problem which I cannot find an answer to) how do I create a selector for the ids which include angled brackets, since jquery uses these for a attribute selector.
EDIT: Answer use \ to escape the brackets i.e. $('#id\\[0\\]')
how do I change the ids within the tree.
EDIT: I have created a function as follows:
function updateAttributes(clone, count) {
var f = clone.find('*').andSelf();
f.each(function (i) {
var s = $(this).attr("id");
if (s != null && s != "") {
s = s.replace(/([^\[]+)\[0\]/, "$1[" + count + "]");
$(this).attr("id", s);
}
});
This appears to work when called with the cloned set and the count of existing versions of that set. It is not ideal as I need to perform the same for name and for attributes. I shall continue to work on this and add an answer when I have one. I'd appreciate any further comments on how I might improve this to be generic for all tags and attributes which asp.net MVC might create.
how do I clone from a template i.e. not from an active fieldset which has data already entered, or return fields to their default values on the cloned set.
You could just name the input field the same for all entries, make the select an input combo and give that a consistent name, so revising your code:
<div id='ContactDetails'>
<div class='ContactDetailsEntry'>
<select id="venue_ContactLink_ContactDatas_Type" name="venue_ContactLink_ContactDatas_Type"><option>Email</option>
<option>Phone</option>
<option>Fax</option>
</select>
<input id="venue_ContactLink_ContactDatas_Data" name="venue_ContactLink_ContactDatas_Data" type="text" value="" />
</div>
</div>
<p>
<input type="submit" name="SubmitButton" value="AddContact" id='addContact'/>
</p>
I'd probably use the Javascript to create the first entry on page ready and then there's only 1 place to revise the HTML.
When you submit, you get two arrays name "venue_ContactLink_ContactDatas_Type" and "venue_ContactLink_ContactDatas_Data" with matching indicies for the contact pairs, i.e.
venue_ContactLink_ContactDatas_Type[0], venue_ContactLink_ContactDatas_Data[0]
venue_ContactLink_ContactDatas_Type[1], venue_ContactLink_ContactDatas_Data[1]
...
venue_ContactLink_ContactDatas_Type[*n*], venue_ContactLink_ContactDatas_Data[*n*]
Hope that's clear.
So, I have a solution which works in my case, but would need some adjustment if other element types are included, or if other attributes are set by with an index included.
I'll answer my questions in turn:
To select an element which includes square brackets in it's attributes escape the square brackets using double back slashes as follows: var clone = $("#contactFields\[0\]").clone();
& 3. Changing the ids in the tree I have implemented with the following function, where clone is the variable clone (in 1) and count is the count of cloned statements.
function updateAttributes(clone, count) {
var attribute = ['id', 'for', 'name'];
var f = clone.find('*').andSelf();
f.each(function(i){
var tag = $(this);
$.each(attribute, function(i, val){
var s = tag.attr(val);
if (s!=null&& s!="")
{
s = s.replace(/([^\[]+)\[0\]/, "$1["+count+"]");
tag.attr(val, s);
}
});
if ($(this)[0].nodeName == 'SELECT')
{ $(this).val(0);}
else
{
$(this).val("");
}
});
}
This may not be the most efficient way or the best, but it does work in my cases I have used it in. The attributes array could be extended if required, and further elements would need to be included in the defaulting action at the end, e.g. for checkboxes.