In JQuery, How can I avoid two $(this) selectors from causing conflict to each other as part of an each() method? - jquery-selectors

everybody.
I'm sort of newbie in JQuery and I'm trying to do the following: I've got both select and checkbox elements and I want certain changes to occur each time the document is loaded or refreshed. What I mean is, the background of the selects should be different depending on whether an empty option (or prompt) is currently selected or not. Also, depending on the former, an (accompanying) checkbox should be enabled or disabled as well (empty option selected => checkbox disabled OR the other way around).
Now, I won't be inputting any ids (from selects or checkboxes) manually. Instead, I want to get them all dinamically by using an each method on the correct selector. And that's where the problem arises.
So, lets say we've got this:
<select id="select_01">
<option value="">Whatever as prompt...</option>
<option value="first">First option</option>
</select>
<input id="check_box_01" type="checkbox" />
<select id="select_02">
<option value="first" selected="selected">First option</option>
<option value="">Whatever as prompt...</option>
</select>
<input id="check_box_02" type="checkbox" />
and in a script I put this:
$(document).ready(function() {
$("select,:checkbox").each(function() {
mySelectId = $("#" + $(this).attr("id"));
myCheckboxId = $("#" + $(this).attr("id"));
if (mySelectId.attr("value") === "") {
mySelectId.css({
"background": "grey"
});
myCheckboxId.attr("disabled", "disabled");
}
});
});
The problem, as you can see, is the $("select,:checkbox").each method not being able to discern which $(this) represents what (well, that and my own obvious lack of knowledge to solve this problem).
If I leave one of the selectors out, everything works well but (obviously) it only affects selects or checkboxes, but not both. Something like this works (the background changes as it should but checkboxes are left unaffected):
$(document).ready(function() {
$("select").each(function() {
mySelectId = $("#" + $(this).attr("id"));
if (mySelectId.attr("value") === "") {
mySelectId.css({
"background": "grey"
});
}
});
});
¿Can I make mySelectId and myCheckboxId two different, easily recognizable variables within the scope of the method? Any help will be greatly appreciated. Thanks in advance!
Carlos Pardilla.
PD: (I wanted to say "Hi everybody" on top, but the edits keep on cutting the whole greeting - dont' know why)

In your loop, the each() function, you do not get the elements as pairs.
You get them one-by-one.
I would do it in two steps (two loops), one for the selects, one for the checkboxes.

if ( $(this).is("select") ) {
mySelectId = $("#" + $(this).attr("id"));
}else if (( $(this).is(":checkbox") ) {
myCheckboxId = $("#" + $(this).attr("id"));
}

Well, I finally managed to get it working thanks to the both of you:
$(document).ready(function() {
$("select, :checkbox").each(function() {
if ($(this).is("select")) {
mySelectId = $("#" + $(this).attr("id"));
if (mySelectId.attr("value") === "") {
mySelectId.css({
"background": "grey"
});
}
} else if ($(this).is(":checkbox")) {
myCheckboxId = $("#" + $(this).attr("id"));
if (mySelectId.attr("value") === "") {
myCheckboxId.attr("disabled", "disabled");
}
}
});
});
The only thing I do not find myself entirely confortable with is with having to repeat if (mySelectId.attr("value") === "") {, (I try to apply Rails's DRY philosophy when possible), but I'm sure I'll find a workaround.
I find the is() method (wich I wasn't previously too familiar with) to be of much help in situations such as these.
Feel free to tinker with this as you'll please: http://jsfiddle.net/CarlosPF/DsHUp/
Many thanks to you both! Your help has been invaluable.
Carlos Pardilla.

Related

Resetting form and input fields

So I am making a simple todo list app with Framework7 and VueJS, but I'm struggling to understand how to reset the input fields.
<f7-list id="todo-form">
<f7-list-input id="item-input" type="text" name="listitem">
</f7-list-input>
</f7-list>
<f7-button #click="newItem">Add task</f7-button>
newItem() {
let formData = this.$f7.form.convertToData('#todo-form');
this.listItemName = formData.listitem;
if (this.listItemName == '' || this.listItemName === undefined) {
return false;
} else {
this.listItems.push(this.listItemName);
console.log(this.$$('#item-input')); // What to do here?
}
}
I would like to reset the item-input field as I click the button. I tried using Dom7 (not jQuery!) for this but there seems to be nothing storing the form input value.. After googling all I could find is suggestions to do $$('#item-input').val('') but there is no .val when I look through the element in the console.
Help is, as always, much appreciated!

knockout.js - help dealing with UI state changes when polling for updates

I'm having a problem losing UI state changes after my observables change and was hoping for some suggestions.
First off, I'm polling my server for updates. Those messages are in my view model and the <ul> renders perfectly:
When my user clicks the "reply" or "assign to" buttons, I'm displaying a little form to perform those actions:
My problem at this point was that when my next polling call returned, the list re-binds and I lose the state of where the form should be open at. I went through adding view model properties for "currentQuestionID" so I could use a visible: binding and redisplay the form after binding.
Once that was complete, the form displays properly on the "current item" after rebinding but the form values are lost. That is to say, it rebinds, rebuilds the form elements, shows them, but any user input disappears (which of course makes sense since the HTML was just regenerated).
I attempted to follow the same pattern (using a value: binding to set the value and an event: {change: responseChanged} binding to update an observable with the values). The HTML fragment looks like this:
<form action="#" class="tb-reply-form" data-bind="visible: $root.showMenu($data, 'reply')">
<textarea id="tb-response" data-bind="value: $root.currentResponse, event: {keyup: $root.responseChanged}"></textarea>
<input type="button" id="tb-submitResponse" data-bind="click: $root.submitResponse, clickBubble: false" value="Send" />
</form>
<form action="#" class="tb-assign-form" data-bind="visible: $root.showMenu($data, 'assign')">
<select id="tb-assign" class="tb-assign" data-bind="value: $root.currentAssignee, options: $root.mediators, optionsText: 'full_name', optionsValue: 'access_token', optionsCaption: 'Select one...', event: {change: $root.assigneeChanged}">
</select>
<input type="button" id="tb-submitAssignment" data-bind="click: $root.submitAssignment, clickBubble: false" value="Assign"/>
</form>
Now, I end up with what seems like an infinite loop where setting the value causes change to happen, which in turn causes value... etc.
I thought "screw it" just move it out of the foreach... By moving the form outside of each <li> in the foreach: binding and doing a little DOM manipulation to move the form into the "current item", I figured I wouldn't lose user inputs.
replyForm.appendTo(theContainer).show();
It works up until the first poll return & rebind. Since the HTML is regenerated for the <ul>, the DOM no longer has my form and my attempt to grab it and do the .appendTo(container) does nothing. I suppose here, I might be able to copy the element into the active item instead of moving it?
So, this all seems like I'm missing something basic because someone has to have put a form into a foreach loop in knockout!
Does anybody have a strategy for maintaining form state inside a bound item in knockout?
Or, possibly, is there a way to make knockout NOT bind anything that's already bound and only generate "new" elements.
Finally, should I just scrap knockout for this and manually generate for "new items" myself when each polling call returns.
Just one last bit of info; if I set my polling interval to something like 30 seconds, all the bits "work" in that it submits, saves, rebinds, etc. I just need the form and it's contents to live through the rebinding.
Thanks a ton for any help!
Well, I figured it out on my own. And it's embarrassing.
Here is a partial bit of my VM code:
function TalkbackViewModel( id ) {
var self = this;
talkback.state.currentTalkbackId = "";
talkback.state.currentAction = "";
talkback.state.currentResponse = "";
talkback.state.currentAssignee = "";
self.talkbackQueue = ko.observableArray([]);
self.completeQueue = ko.observableArray([]);
self.mediators = ko.observableArray([]);
self.currentTalkbackId = ko.observable(talkback.state.currentTalkbackId);
self.currentAction = ko.observable(talkback.state.currentAction);
self.currentResponse = ko.observable(talkback.state.currentResponse);
self.currentAssignee = ko.observable(talkback.state.currentAssignee);
self.showActionForm = function(data, action) {
return ko.computed(function() {
var sameAction = (self.currentAction() == action);
var sameItem = (self.currentTalkbackId() == data.talkback_id());
return (sameAction && sameItem);
}, this);
};
self.replyToggle = function(model, event) {
// we're switching from one item to another. clear input values.
if (self.currentTalkbackId() != model.talkback_id() || self.currentAction() != "reply") {
self.currentResponse("");
self.currentAssignee("");
self.currentTalkbackId(model.talkback_id());
}
My first mistake was trying to treat the textarea & dropdown the same. I noticed the dropdown was saving value & reloading but stupidly tried to keep the code the same as the textarea and caused my own issue.
So...
First off, I went back to the using the $root view model properties for currentAssignee and currentResponse to store the values off and rebind using value: bindings on those controls.
Next, I needed to remove the event handlers:
event: { change: xxxChanged }
because they don't make sense (two way binding!!!!). The drop down value changes and updates automatically by using the value: binding.
The textarea ONLY updated on blur, causing me to think I needed onkeyup,onkeydown, etc. I got rid of those handlers because they were 1) wrong, 2) screwing up the value: binding creating an infinite loop.
I only needed this on the textarea to get up-to-date value updates to my viewmodel property:
valueUpdate: 'input'
At this point everything saves off & rebinds and I didn't lose my values but my caret position was incorrect in the textarea. I added a little code to handle that:
var item = element.find(".tb-assign");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
Some browsers behave OK if you just do item.focus().val(item.val()); but i needed to actually cause the value to "change" in my case to get the caret at the end so I saved the value, cleared it, then restored it. I did this in the event handler for when the event data is returned to the browser:
$(window).on("talkback.retrieved", function(event, talkback_queue, complete_queue) {
var open_mappings = ko.mapping.fromJS(talkback_queue);
self.talkbackQueue(open_mappings);
if (talkback_queue) self.queueLength(talkback_queue.length);
var completed_mappings = ko.mapping.fromJS(complete_queue);
self.completeQueue(completed_mappings);
if (self.currentTalkbackId()) {
var element = $("li[talkbackId='" + self.currentTalkbackId() + "']");
if (talkback.state.currentAction == "assign") {
var item = element.find(".tb-assign");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
} else {
var item = element.find(".tb-response");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
}
}
}
);
So, my final issue is that if I used my observables in my method "clearing" the values when a new "current item" is selected (replyToggle & assignToggle), they don't seem to work.
self.currentResponse("");
self.currentAssignee("");
I cannot get the values to clear. I had to do some hack-fu and added the line below that to just work around it for now:
$(".tb-assign").val("");

Why is IOS 7.0.3 picker goofing up my LiveCode webform?

I don't know where the best place to ask about this is. My problem seems to be with IOS 7.0.3 and how Safari is handling the picker in a web form. I've created a web form with LiveCode that works just fine in every browser I've tried. But on the iPhone, the picker malfunctions. If you choose one item and press Done, it reverts to 0 items chosen. If you choose two items and press done, it shows one item chosen. The same goes for three, four, and so on. Has anyone else had this experience? Here is a snippet of one of the multiple choice buttons.
<label for="authors[]">
Select Author(s)
<select name="authors[]" id="authors" multiple="yes" size="7" >
<?lc
put the number of lines in tAuthorList into tTotalAuthors
repeat with x = 1 to tTotalAuthors
put "<option value=" & q(line x of tAuthorList)
put lineOffset(line x of tAuthorList,tPrevAuthors) into tLineHit
if bDataSubmitted and line x of tAuthorList is line tLineHit of tPrevAuthors then
put " selected"
end if
put ">" & line x of tAuthorList & "</option>" & return
end repeat
?>
</select>
</label>
This is the URL:
http://lc.scs.earlham.edu/soul_therapy3.lc
Incidentally, I use it with an iframe in my Drupal 7 site:
http://soulshare.org/soul_therapy/tool
There is an issue (design choice?) in iOS7 where you need to not only scroll to the correct value but also tap your selected value. I don't know if this is your problem though...
In some forms you only needs to scroll to the correct value but in many, you need to scroll and THEN select. As you probably have guessed this has nothing to do with LiveCode...
This is a bug in IOS and has been reported to apple. Currently the best solution I've found uses jQuery to fix the selected items upon closing the picker. Simply paste this into your JS file and away you go.
// hack for iPhone 7.0.3 multiselects bug
if(navigator.userAgent.match(/iPhone/i)) {
$('select[multiple]').each(function(){
var select = $(this).on({
"focusout": function(){
var values = select.val() || [];
setTimeout(function(){
select.val(values.length ? values : ['']).change();
}, 1000);
}
});
var firstOption = '<option value="" disabled="disabled"';
firstOption += (select.val() || []).length > 0 ? '' : ' selected="selected"';
firstOption += '>« Select ' + (select.attr('title') || 'Options') + ' »';
firstOption += '</option>';
select.prepend(firstOption);
});
}
Multiple select in Safari iOS 7

Javascript focus event goes to next form field

I am fairly new to Javascript and have a basic question. I have an HTML form with first_name and last_name input fields. I have the following Javascript code in the header but after the code runs, the focus goes to the next field (last_name). Why is that and how do I correct it?
Thank you.
<script>
function validateForm()
{
valid = true;
//validate first name
if (document.contactform.first_name.value == "")
{
//alert user first name is blank
alert("You must enter a first name");
document.getElementById("first_name").focus();
return false;
}
return valid;
}
</script>
and the form field code is:
input type="text" name="first_name" id="first_name" maxlength="50" size="30" onBlur="validateForm()"
A fix for this is to add a slight delay.. like so:
setTimeout(function() {
document.getElementById('first_name').focus()
}, 10);
Here is your example with this fix in jsfiddle: http://jsfiddle.net/FgHrg/1/
It seems to be a common Firefox problem.. I don't know exactly why but it has something to do with Firefox loading the javascript before the DOM is fully loaded.. in otherwords getElementById('first_name') returns null. But adding the slight delay fixes this problem.

How do I set two mutually exclusive check boxes in Jquery?

$(document).ready(function() {
$("input:txtAge1").click(function(event) {
if ($(txtAge1).attr("checked") == true) {
$(txtAge2).attr("checked", "unchecked");
$(txtAge2).attr("checked") == false)
}
if ($(txtAge2).attr("checked") == true) {
$(txtAge1).attr("checked", "unchecked");
$(txtAge1).attr("checked") == false)
}
});
});
<input type="checkbox" id="txtAge1" name="option1" value=""/>21<br>
<input type="checkbox" id="txtAge2" name="option2" value=""/>55<br>
I am trying to select either one checkbox or the other. So if one box is UNchecked, it should either be not allowed or force the
other box to BE checked ...in other words, enforce either one or the other but never allow
a "undefined" condition
Maybe I'm dumbing down the issue a bit, but why not try using radio buttons?
You can set one to be selected to avoid the user submitting an empty value.
Update: Since your customer wants checkboxes, here's a solution in jQuery:
$(document).ready(function(){
$('.radioButton').click(function() {
$('.radioButton').prop("checked", false);
$(this).prop("checked", true);
});
});
That's the jQuery code. You should set your input boxes up like this:
<input type="checkbox" id="txtAge1" class="radioButton" name="option1" value=""/>21
<input type="checkbox" id="txtAge2" class="radioButton" name="option2" value=""/>55
That should work, but it's untested. I might've missed something.
One solution is to add two click events, one for each checkbox. When one is clicked, the other is unclicked.
$("#checkbox1").click(function() {
$("#checkbox2").prop('checked', false);
});
$("#checkbox2").click(function() {
$('#checkbox1').prop('checked', false);
});
I ran into this issue recently, except I needed checkboxes instead of radio buttons as having both options unchecked was a requirement. I resolved it with something like this (adapted to the OP's code):
<input type="checkbox" id="txtAge1" />21
<input type="checkbox" id="txtAge2" />55
$(document).ready({
$("#txtAge1").click(function() {
if($("#txtAge1").is(':checked')) {
$("#txtAge2").prop('checked', false);
}
});
$("#txtAge2").click(function() {
if($("#txtAge2").is(':checked')) {
$("#txtAge1").prop('checked', false);
}
});
)};
Might not be that pretty, but it works.
I also wanted to note the excellent link http://rndnext.blogspot.com/2009/08/mutually-exclusive-html-select-elements.html here.
One caution though, I used it to mutex two dynamically generated select lists inside a div. . Since the content to be manipulated is not available at page load, it was not working as expected. Following solutions at jQuery - selecting dynamically created divs helped resolve the issue.
I would use this function. Allows mutual exclusion and allows uncheking:
$('#divId').find(':checkbox').click(function() {
var state=$(this).prop("checked");
$(':checkbox').prop("checked", false);
$(this).prop("checked", state);
});
$("input[type=checkbox]").click(function () {
$(this).siblings().prop("checked", false);
})