I have a text input and on focusout, I have couple of select elements which I want to fill with the text field's value.
And I have bunch of select tags with 'NameSelect' class
$('.textField').focusout(function() {
var name = $(this).val();
var NameOption = $('<option>', { value: name, text: name, attrid: '1'});
var selects = $('#mainForm').find('.NameSelect');
$(selects).each(function(i, obj) {
console.log($(obj)); // it seems to get the right select
$(obj).append(NameOption);
})
}
However, when I do that, even though the selects get all the right elements and for loop for the right count, it only appends the option input to the latest object, not all of them.
What am I missing here?
The issue is because NameOption holds a reference to the option, hence if you append() it multiple times it will move between each parent element.
To fix this you can either clone() the element when you append it:
selects.append(NameOption.clone());
Or you could just provide append() with a string to create a new element each time it's called:
$('.textField').focusout(function() {
var name = $(this).val();
$('#mainForm').find('.NameSelect').append('<option value="' + name + '" attrid="1">' + name + '</option>');
})
});
Note that in both cases the each() is not required.
Related
In the beginning I have JSON data, and I need to convert and output the list to html.
var frut =
{
"wtfrut": [
["0x01", "Apple"],
["0x02", "Orange"],
["0x03", "Pineapple"],
["0x04", "Banana"]
],
[other irrelevant elements]
}
I made it an html <select> plus list of <options> . . .
<select>
<option data-index="0x01">Apple</option>
<option data-index="0x02">Orange</option>
<option data-index="0x03">Pineapple</option>
<option data-index="0x04">Banana</option>
</select>
. . . and stuck it in a js variable.
This <select> list is a cell in a table, and needs to appear in a couple hundred rows.
While building the table, when I need to display the dropdown, I need to go back thru and find the selected attribute of each <select><option>
Problem 1)
The best I can get from
var select = document.createElement("select");
var options = document.createElement("option");
options.setAttribute("value", element[1]);
...
select.appendChild(options);
return select;
is [object HTMLSelectElement] where the dropdown was supposed to be. return select.value returns the value attribute of the first item on the list.
Therefore, I have resorted to stuffing var dropDown with raw html.
out += "<option value=\"" + element[1] + "\" data-hex = \"" + element[0] + "\" data-index = \"" + index + "\">";
because it works. dropDown winds up with the <select> and all <option>s. And it works when I call it with
"<td class=\"vkeyName\" data-f4key-index = \"+index+\">" + dropDown + "</td>"
Problem 2)
Now that that's working, I try to take dropDown back to js at render time (during the loop that produces the above <td>) and figure out which <option> needs to be chosen as default for the dropdown. select.length returns the string length which I understand. It's just a js string.
Overall
What I don't understand is how to get data over the threshold between js variable and valid html element, in either direction. To make that js string into a list of valid html elements that can be output to the html page... Or to take valid html elements, put them into a variable to be worked by js.
getElementBy* and document.write doesn't work. I presume because I don't have the document yet, I'm building objects.
At this point I'm uninterested in js libraries and helpers. This is a learning project and I want to understand this so that things like jQuery aren't so magical.
I made a small example of a way how you could do create a combobox that generates an Array of some kind of data, and how you could help out yourself by using some callback functions to get the value and the text, and how to choose which element should be preselected, and how you could react on changes in the html element.
You can always use document.getElementById, but you have to wait until you are sure that the page got loaded, one way to do it, is to wait for the window.onload function to fire (which means that the DOM is ready to be manipulated, scripts and css are loaded)
In vanilla javascript, you can do it by registering a callback function on the load event, like this:
window.addEventListener('load', function() { ... });
To generate the combobox, I made a small helper namespace and added a comboBoxGenerator, that takes an object in, and generates the combobox in your desired targetElement.
I then iterate the data and for each element, get the value and text over a callback function (that you define when you called the generator) and it returns the value and the text for that single option. It also determines if the element should be preselected.
By registering to the change event of the combobox, you can then find out which element was actually selected, and for that I also added a small function that displays that the function got changed
The 'use strict;' statement helps to add for example forEach function to the array, and will help you to keep your code more clean
I also documented the source a bit, so that you hopefully understand what everything is doing :)
'use strict';
var helper = {};
(function(ns) {
function comboBoxGenerator(options) {
// get the element that you are targetting
var el = document.getElementById(options.target),
cmb = document.createElement('select'),
option;
// iterate the data, and for each element in the array, create an option and call the defined callback functions
options.data.forEach(function(item) {
option = document.createElement('option');
option.value = options.valueSelector(item);
option.text = options.textSelector(item);
option.selected = options.isSelected(item);
// add the option to the combobox
cmb.appendChild(option);
});
// listen to changes on the combobox and then call the selectionChanged event
cmb.addEventListener('change', function(e) {
// this = cmb because of the bind statement on below
// call the selectionChanged callback function, and assing the cmb as the this for the callback function (.apply(this, ...))
options.selectionChanged.apply(this, [this.options[this.selectedIndex]]);
}.bind(cmb));
el.appendChild(cmb);
}
// set the combo function on the helper by either reusing an existing function, or the function just written above
ns.combo = ns.combo || comboBoxGenerator;
}(helper));
// wait till all resources are loaded, and then generate the combobox
window.addEventListener('load', function() {
var dummyData = {
"wtfrut": [
["0x01", "Apple"],
["0x02", "Orange"],
["0x03", "Pineapple"],
["0x04", "Banana"]
]
}, selectedValue = "0x03";
// call the helper method with an object defining the data, targetelement, and callback functions
helper.combo({
target: 'myTable',
data: dummyData.wtfrut,
valueSelector: function(item) {
// item would be like ["0x01", "Apple"], return "0x01" for value
return item[0];
},
textSelector: function(item) {
return item[1];
},
isSelected: function(item) {
// check if the item matches a selectedValue if so, return true, not false
return item[0] === selectedValue;
},
selectionChanged: function(item) {
// gets called when the selection is changed, item = Option, value is the current value, this = combobox
selectedValue = item.value;
console.log('selectedValue changed to ' + selectedValue + ' index = ' + this.selectedIndex);
}
});
});
<div>
<div id="myTable">
</div>
</div>
I am trying to set up a system such that when a button is clicked, the value of an input field is adjusted.
The ID of the field to be adjusted is equal to the name of the sibling element of the button.
As such, I'm attempting to code:
On button click
- Get sibling's name
- Get element with ID equal to siblings name
- Change value of that element
Here is my attempt:
$(function() {
$('#values').on('click', '.remField', function() {
var = $(this).siblings().name()
$('#var').val('DELETE');
return false;
});
});
However this does not work.
Your selector creation and retrieval of name is incorrect try:
$('#values').on('click', '.remField', function() {
var name = $(this).siblings().attr('name');
$('#' + name).val('DELETE');
return false;
});
Note that siblings may return multiple items as a collection and attr on a collection will get the name of the 1st item in the collection. If you know it is the next of prev element then better use .next() or .prev() to be more specific. var is a keyword you can't use that as a variable name.
Use .attr()
$('#values').on('click', '.remField', function () {
var name = $(this).siblings().attr('name');
$('#' + name).val('DELETE');
return false;
});
.siblings() will return multiple items
I have almost completed this but am stuck on an advanced selector. I am trying to select a label next to a radio button, but its a little more complex than that.
I have a select box. Only radio buttons (and their sibling labels) in which the value of the select matches the name (well part of) of the radio button.
I have a JS fiddle set up, what I am looking to do, is on selection of January, everything except Jan should be hidden, and when i select February, it should change. I'm trying to do this with just the name and no additional classes but if it comes down to it, I can add them.
http://jsfiddle.net/alpha1beta/G9Sz2/2/
Below is my working selector to get the radio button, and now am looking at how to get their + label next to it
$('.radio([name="insights[interview_questions[' + curr_sel + ']]"])').css('display','inline');
Thanks in advance!
Try
var $radios = $('.radio'), $all = $radios.add($radios.nextUntil('.radio'));
$("#interview_month").change(function () {
var $mon = $radios.filter('.radio[name="insights[interview_questions[' + this.value + ']]"]');
var $cur = $mon.add($mon.nextUntil('.radio')).show();
$all.not($cur).hide()
}).change();
Demo: Fiddle
You may want to check the next() function, it returns the next sibling
Try this:
$("#interview_month").change(function () {
$('.radio , label').hide();
var sel = '.radio[name*="' + $("#interview_month").val() + '"]';
$(sel).add(sel+'+label').css('display', 'inline');
});
I have a form where I add inputs dynamically. When I add a new input I increment a global id variable and concatenate it to the input's id so it will be unique. I also create a delete button for each input that should remove that input and himself (through removing the container <div> they are in). I do the remove process by adding an anonymous function to the delete button's click event via JQuery:
$('#deletebutton' + id).click(function(){
$('#inputcontainer' + id).remove();
});
The only problem with this solution that it isn't work in the way I excepted it. When I click any of the delete buttons it will delete the last input container because when I click, it executes the anonymous function and evaluate the id variable at that time, so the selected id will be the last input's id. So always the last input container will be deleted.
Is there a way to rewrite this function so when I add it to the click event, than it will evaluate the id, inject it and handle the selection as if it had been written like #inputcontainer1, #inputcontainer2, etc.
I can make this by adding the function's body to the button's onclick() event:
var newbutton = '<button id="deletebutton' + id + '" type="button" onclick="javascript:$(\'#inputcontainer' + id + '\').remove();">x</button>';
But is there a way doing this with the JQuery click() way?
To answer the specific question, you'd have to dig the id out of the DOM:
$('#deletebutton' + id).click(function(){
var id = $(this).attr("id").replace('deletebutton','');
$('#inputcontainer' + id).remove();
});
You could also store it as data when you create the delete button:
<button data-id="1" id="deletebutton1">
$('#deletebutton' + id).click(function(){
var id = $(this).data("id");
$('#inputcontainer' + id).remove();
});
Note that in both of these cases, id is a string, not an integer.
When I click any of the delete buttons it will delete the last input container [...]
If your 1st snippet is inside a loop, id probably isn't being scoped to each iteration. So, by the time one of the click() events is triggered and it's trying to use .remove(), id will have already been set to the last value given while looping.
You can use an IIFE to create an additional function scope for keeping a different id for each iteration (ref: closure).
/* loop */ {
var id = ...;
(function (id) {
$('#deletebutton' + id).click(function(){
$('#inputcontainer' + id).remove();
});
})(id);
}
Though, for future reference, ECMAScript 6 is adding block scoping which should allow for:
/* loop */ {
let id = ...;
$('#deletebutton' + id).click(function(){
$('#inputcontainer' + id).remove();
});
}
$('#deletebutton' + id).click(function(){
$(this).parent().remove();
});
If the container isn't a direct parent and doesn't have a class you could do:
$('#deletebutton' + id).click(function(){
var idNum = $(this).attr("id").replace('deletebutton','');
$("#inputcontainer"+idNum).remove();
});
If you've got appropriate classes (or can add them), this would be best:
$(document).on("click",".deleteButton",function() {
$(this).parents(".inputContainer").remove();
});
How can I locate the tag which calls a JQuery script, when
the tag is dynamically loaded, so won't be the last
tag on the page?
I'm using the MagicSuggest autosuggest library. I want to give certain suggested items a different background color depending on their contents, which I'm currently doing by adding JQuery inside a tag, which I'm adding on to the String which is returned to be rendered inside the selection div. Then, to get the div the item is suggested in, I need to essentially get the parent() of the tag, and change it's css() properties. How can I get this current script tag however?
I'm currently assigned each new tag an id generated from incrementing a JS variable - which works, but isn't very 'nice'! Is there anyway I can directly target the tag with JQuery?
If it perhaps makes it clearer, here is my current selectionRenderer function.
selectionRenderer: function(a){
var toRet = a.english;
var blueBgScript = "<script id=ft" + freeTextFieldID + ">$('#ft" + freeTextFieldID + "').parent().css('background', 'blue');</script>"
if(a.id==a.english){
toRet += blueBgScript;
freeTextFieldID++;
}
return toRet;
},
Why don't you add some code at afterrender event instead? Add some tag to flag the options that need a different background, then detect the parents and add a class (or edit the bg property) or whatever you like:
var newMS = $('#idStr').magicSuggest({
data: 'states.php',
displayField: 'english',
valueField: 'id',
selectionRenderer: function(a){
var toRet = a.english;
if(a.id==a.english) toRet = "<span class='freetext'>" + toRet + "</span>";
return toRet;
},
});
$(newMS).on('selectionchange', function(event,combo,selection){
var selDivs = $(event.target._valueContainer[0].parentNode).children('div'); //Get all the divs in the selction
$.each(selDivs,function(index,value){ //For each selected item
var span = $(value).children('.freetext'); //It if contains a span of class freetext
if(span.length == 1) $(value).css('background','blue'); //Turn the background blue
});