So I've written some code to sort all my site's select menus, and it works perfectly in every browser we support... except Firefox.
http://jsfiddle.net/vkjAC/6/
My code takes in the options for a select element, sorts them, and returns them. Somewhere in there, the selectedIndex on the select element changes to the last item.
I check what values are selected/defaultSelected:
for(k=0; k<options.length; k++)
{
if(options[k].defaultSelected == true)
{
sel = k;
break;
}
}
if(sel === null)
{
for(k=0; k<options.length; k++)
{
if(options[k].selected == true)
{
sel = k;
break;
}
}
}
if(sel === null)
{ options[0].selected = true; }
else
{ options[sel].selected = true; }
But I can't set the selectedIndex from this function because I'm not passing in the entire select object, just the option list.
I tried looking up similar problems, but every other thread I saw said it was a caching problem, or that I needed to add autocomplete="off", but those didn't work. I assume it has something to do with my code, but I haven't modified the selectedIndex property anywhere.
Any suggestions? I'm losing my mind (and running out of time!)
I've had this before. It has something to do with the fact that the options are removed then added again. You have to re-select the value after sorting. For example:
var ops = $('#mass').find('option');
$('#mass').prepend(sortDropDown(ops)).val($("#mass > option[selected]").val());
Related
I currently have a table with a function that checks certain columns that have checkboxes, whenever they are "clicked off", to see if they're all completely empty.
The function does a loop from the first of the checked columns up until the last one that has to be checked. They go from 1 to 8, and their ids go from "f01_check" up until "f08_check". If all are empty, it adds a css class to its description column that changes background color.
The function looks like this:
function unChecked(rowNumber) {
alert(rowNumber);
var i = 1;
var check = false;
// Column loop
while (i < 9 && check == false) {
if (rowNumber.getElementById("f0" + i + "_check").checked == false) {
i++;
} else {
check = true;
}
// If all checkboxes are empty, add class
if (i == 9 && check == false) {
$s(description.addClass(emptyRecords));
}
}
}
The Dynamic Action leading to this function and its parameter is this:
var row = this.triggeringElement.closest('tr');
unChecked(row);
I realize that the rowNumber.getElementById doesn't work, but I can't figure out how to link or connect them. The way I'm looping through the columns might be a rough attempt, but it works. I've tested it on a set column and it it does stop whenever the loop reaches a checked checkbox. The problem is I just can't dynamically set the row to match the one that I've clicked.
I've tried getting the property .rowIndex as well, but I can't figure out where to use it, even if I do get the correct value.
I found a different solution, that also somewhat solves a different problem in the future.
Rather than use rownum to set a dynamic id for the columns, I changed it to it's primary key:
apex_item.checkbox (1, '1_' || pk, decode(data1,null,null,'CHECKED'), null, null, 'f01_' || pk) as lpb1
This basically helped me on knowing which row was being clicked, because the id also held it's primary key. This allowed me to use it's primary key value to improve the function. The item_pkis an item that gets its value from the corresponding table column:
function unChecked(row) { // var row = this.triggeringElement.closest('tr').rowIndex;
// unChecked(row);
var i = 1;
var check = false;
while (i < 9 && check == false) {
if (document.getElementById("f0" + i + "_" + item_pk.value).checked == false) {
i++;
} else {
check = true;
}
if (i == 9 && check == false) {
//addClass Segment
}
}
}
So if the report were to be filtered, the row number wouldn't match what the filter actually showed on the report table.
The previous solution was also fixed by removing the case clause from the select, as follows:
apex_item.checkbox (1, '1_' || a.pk, case when max(decode(data1,1,1,null)) is null then '' else 'CHECKED' end, null, null, 'f01_chk') as lpb1,
But again, the first solution showed actually foresees and handles a filtered report, so I believe it's a superior and more accurate solution.
Now I just need to complete the addClass segment. For some reason Apex is setting the field "static id" as the column header, which is slightly problematic since I meant to use the document.getElementById. But at least the main problem has been fixed. Thanks for the responses.
I have an input field where the user inputs their zip code which I then attempt to match to a zip code within an array. While the following works, I need it to actually show an alert dialog once saying that zip code wasn't found, however, it current pops up a lot even if the zip code is in the list.
Here's my snippet:
_zipcodeBtn.onclick = function(){
var userZip = _zipcode.value;
for (var i = 0; i < zipcode_list.length; i++) {
if (zipcode_list[i] === userZip) {
console.log('works');
break;
} else {
alert("repeating");
}
}
};
I want it to check if their zip is available without it also repeating the else statement multiple times. Whats the best way to prevent this?
There's an easier way to find an item in an array, by referencing the array's indexOf() method (docs).
if (zipcode_list.indexOf(userZip) != -1) {
//found - do something
} else
alert('Zip not found!');
As Angel Politis shows, you can also use includes() if you literally just want to know whether an item is in an array, not its actual position within it.
Sidenote: it's important to check against -1 when using indexOf() because it returns the index at which the search is found - or -1 if it's not. If the search is found at the first key, this is 0, and 0 is a falsy value, which can catch people out sometimes when they do things like this:
var arr = [1, 2, 3];
if (arr.indexOf(1))
alert('success');
else
alert('failed');
You'd think the success alert would fire here, but actually it'll be the failure alert, because indexOf() in this case returns 0, and 0 is a falsy value when it's interrogated in a condition.
There's no need to use a loop here. You can just say:
if (!zipcode_list.includes(userZip)) alert("Doesn't work!");
If you must use a loop, then just set a flag by default to false and then, if the zip is found set it to true. Then you can check it outside the loop:
/* Create a flag and set it by default to false. */
var found = false;
/* Loop */
for (var i = 0, l = zipcode_list.length; i < l; i++) {
if (zipcode_list[i] === userZip) {
/* Set the flag to true and stop the loop. */
found = true;
break;
}
}
/* Check whether the zip was found in the array. */
if (found) console.log("Works!");
else alert("Doesn't work!");
Inside the cshtml view I have a dropdown list with 2 possible options. If selection returns "True", a container with 2 datepickers becomes visible, so user can select a daterange. Whenever I switch dropdown option, both datepickers are assigned with respective values.
$("#ExpirationChoice").change(function () {
var val = $(this).val(); //picks up either True or False
if (val) {
if (val === "True") {
$("#expirationDates").show(); //show container with datepickers
$("#BeginDate").val(getFormattedDate(today()));
$("#EndDate").val(getFormattedDate(today().addMonths(6)));
}
else {
$("#expirationDates").hide();
$("#BeginDate").val("");
$("#EndDate").val(getFormattedDate(today()));
}
}
else {
alert("Please select an option of past or future expiration report.");
}
});
Let's assume I picked True from dropdown, selected a 1 month range between 12/23/16 and 01/23/17, clicked the button to generate a table.
Then, I switched dropdown option to False, which caused to hide container and reassign date values to empty and today in datepickers (hidden, thus, can't be controlled by user).
Now, when I switch back to True again, my selections are gone, it again assigns it to a 6-month range (I understand why it does it, I will explain my attempts later).
My question is: How to keep my latest selections from True option whenever I switch?
A challenge through my attempts
I understand exactly why it was behaving the following way. My way of thinking is to store selections of datepickers from True option into variables. However, here is a challenge that I am suddenly running into:
if (val === "True") {
$("#expirationDates").show();
$("#BeginDate").val(getFormattedDate(today()));
$("#EndDate").val(getFormattedDate(today().addMonths(6)));
var start = $("#BeginDate").val();
var end = $("#EndDate").val();
}
I need to initially assign dates to be a 6-month daterange. So far variables store dates properly. However, whenever I switch, date values will be reassigned back to 6 month range, and it turns into an infinite loop.
Pretty much, I would appreciate any direction in terms of the better approach for such problem.
use a flag var
var flag = false; //global variable
var start;//global variable
var end;//global variable
if (val === "True") {
$("#expirationDates").show();
if(flag == false){
$("#BeginDate").val(getFormattedDate(today()));
$("#EndDate").val(getFormattedDate(today().addMonths(6)));
}
else {
$("#BeginDate").val(start);
$("#EndDate").val(end);
}
start = $("#BeginDate").val();
end = $("#EndDate").val();
flag = true;
}
The main challenge of my case was getting an updated value from datepickers. After doing some additional research, I found this post as a hint to resolve my issue. Therefore, instead of getting latest values from .change(), I stored necessary values on .click()
$("#btnExpiringUnitsTable").click(function () {
var e = document.getElementById("ExpirationChoice");
var selectedOption = e.options[e.selectedIndex].value;
//code
if (selectedOption === "True") { //store latest datepicker values in variables from "True" selection
start = $("#BeginDate").val();
end = $("#EndDate").val();
}
//code
});
As for my initial .change(function), it narrowed it down to the following:
$("#ExpirationChoice").change(function () {
var val = $(this).val();
if (val) {
if (val === "True") {
$("#expirationDates").show();
$("#BeginDate").val(start);
$("#EndDate").val(end);
}
else {
$("#expirationDates").hide();
$("#BeginDate").val("");
$("#EndDate").val(getFormattedDate(today()));
}
}
else {
alert("Please select an option of past or future expiration report.");
}
});
From what I see, I will not be able to pick up updated values from .change(), I would have to go with .click().
So the JavaScript code below is what I am using to pull data from our automated marketing software Eloqua. This works for single-select drop downs. What I want it to do is work for multi-select drop downs.
I know that the ProductValue variable works. So with that said I am positive that it is in the if(ProductValue == ProductList[i].value) specifically the " .value " since this is calling the value on in the drop-downs. Is there a way to make this multiple? This has been driving me nuts for days.
function CustomerInformation()
{
var ProductValue = "<span class=eloquaemail>MarketingCustomerInformation1</span>"; //Field Merge...Field merge is working
var ProductList = document.getElementById('C_Marketing_Customer_Information1').options; //Calling the contact record field
for(var i=0; i< ProductList.length; i++)
{
if(ProductValue == ProductList[i].value)
{
document.getElementById('C_Marketing_Customer_Information1').value = ProductValue;
break;
}
else
{
document.getElementById('C_Marketing_Customer_Information1').value = "Select";
}
}
}
you could use multiple for loops:
var ProductValue = "<span class=eloquaemail>MarketingCustomerInformation1</span>"; //Field Merge...Field merge is working
var ProductList = document.getElementById('C_Marketing_Customer_Information1').options; //Calling the contact record field
for(var i=0; i< ProductList.length; i++)
{
for(var j=0;j<ProductValue.length;j++)
{
if(ProductValue[j] == ProductList[i].value)
{
document.getElementById('C_Marketing_Customer_Information1').option[i].selected="selected";
}
}
}
Could also add a boolean var to select a default for when nothing gets selected.
BASIC IDEA
The first loop will go through all the values in the multi-select list.
The second loop will go through all the values you pass it (meaning ProductValue will need to be an array of all the values you want selected).
IF the current item in the first array (ProductList[i]) is equal to (==) the current item in the second array (ProductValue[j]) THEN you will mark the option as selected.
Some useful tools for your JS needs:
google
W3Schools.com
Firebug (Firefox addon, though most browsers have something tied to F12 that can help, use it to see your variables and step through the functions)
I'm wanting to set the tab order on my forms to go left to right. I've seen the following code around the web
for (var i = 0; i < crmForm.all.length; i++)
{
var element = crmForm.all[i];
if (element.tabIndex && element.tabIndex != "0") {
if (element.className == 'ms-crm-Hidden-NoBehavior')
continue;
if (element.tagName == 'A') {
if (element.className != 'ms-crm-InlineTabHeaderText')
continue;
}
element.tabIndex = 10000 + (i * 10);
}
}
which sets the tab order as i want it. However there is a problem when it comes to currency fields as when you first tab into it the currency symbol is selected, and you can't type anything, and you have to tab again to be able to type anything into the field.
Is there a way for the code to ignore these symbols and go straight into the field itself?
Thanks
Your approach constitutes an unsupported customisation, but with a little manual work you can achieve the same outcome in a fully supported way. All you need to do is add a new "Section" (without showing the header or divider) to your form, for every row of fields.
The result is no unsupported JScript and predictable behaviour that is entirely consistent with the rest of the application.
In my example below I show an example of how I must lay out my form so that native tabbing behaviour "makes sense". However if I wish to use horizontal tabbing, I can rearrange my form, introduce some new sections and then have it work as I want without code.
The beauty of this approach is that it only affects the parts of the form that you want it to.
While technically it's unsupported still, I seem to have fixed the currency problem here:
function TabOrderLefttoRight() {
for (var i = 0; i < crmForm.all.length; i++) {
var element = crmForm.all[i];
if (element.tabIndex && element.tabIndex > "0") { //less than zero instead of !=
if (element.className == 'ms-crm-Hidden-NoBehavior')
continue;
if (element.tagName == 'A') {
if (element.className != 'ms-crm-InlineTabHeaderText')
continue;
}
element.tabIndex = 10000 + (i);
}
}
}
This way it does not affect items that are below 0 in tabindex (currency fields).