JQuery index() - Which way around? - javascript

In the following piece of code:
$('body').on('click', onClickSelector, function(e) {
console.log($(this).index(onClickSelector));
console.log($(onClickSelector).index(this));
}
Both logs seem to give the correct index value. That is, the index of this, within the onClickSelector collection.
But which is technically the correct way to get that value? Or are the two interchangeable? Also, are there any issues that could arise from using one over the other?

But which is technically the correct way to get that value?
They're interchangeable in that instance (since you don't already have the set of matches handy). If you look at the jQuery code under the covers, which looks like this:
index: function( elem ) {
// No argument, return index in parent
if ( !elem ) {
return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
}
// Index in selector
if ( typeof elem === "string" ) {
return indexOf.call( jQuery( elem ), this[ 0 ] );
}
// Locate the position of the desired element
return indexOf.call( this,
// If it receives a jQuery object, the first element is used
elem.jquery ? elem[ 0 ] : elem
);
},
...they end up being the same thing: A call to the internal indexOf passing in a set and an element to find.

Related

adding properties to an object

I wrote this code and it`s simple , i have an empty object which will contain some other objects as properties , but the object stays empty and don`t add the needed properties ..
let buildProfileClientValidator = function(form , rules){
let elements = {};
function init(){
//Some code that works fine
addElement(elementName , elementType);
addElement(elementName , elementType);
//the elements object should now have some other objects as properties
//but it`s empty !!!!
console.log(elements);
}
function addElement(elementName , elementType){
//this condition works fine
if( !elementExist(elementName) ){
//console.log(elementName , elementType); also works fine -> the values of elementName , elementType are present
elements[elementName] = {
type : elementType,
value : '',
rules : (rules[elementName] == undefined) ? '' : rules[elementName].split('|')
};
}
}
}
so what i`m missing ?!
After examining your code, but not knowing how buildProfileClientValidator gets called (and specifically what the rules parameter is), I can trace the problem to this line:
(rules[elementName] === undefined) ? '' : rules[elementName].split('|')
I have modified your code (to make it testable) in the following fiddle (https://jsfiddle.net/hssbsL19/40/) and when I replace that line with a static value, the code works.

jQuery return multiple function

so, i have the following js:
function RHL(a,b,c)
{
return rx.removeClass(a).addClass(b);
return rhpfc.html(parseInt( rhpfc.html() ) -1 );
}
I am having a bit of difficult time with the formatting and syntax.
How do I combine both lines under one return. Also, I want to have two options: -1 or +1. So, I thought I would make - or + as c.
what kind of bracket do I need? (ie. 'c'1)
function RHL(a,b,c){
return [
rx.removeClass(a).addClass(b),
rhpfc.html(parseInt( rhpfc.html() ) -1 )
];
}
then you will need to use the index 0 or 1 to use the return value..
var rx = RHL(a,b,c)[0];
or
var rhpfc = RHL(a,b,c)[1];

lack of identity between jQuery selector and jQuery variable?

I'm running into a maddening problem where I set a variable to point to a jQuery selector, such as: var foobar=jQuery(this); I then pass this variable to a function to be worked on. Let's simplify a little and say the function looks like this:
function SetFieldValue (selector) {
selector.val('test');
console.log ( selector );
console.log ( jQuery('#' + selector.attr('id')) );
}
In this situation if you assume that:
the selector is always a form element (and therefore val() is a valid operation)
the selector does resolve to a single dom element which has an 'id' attribute
You would then expect the two console.log statements to output the same result, right? Well I'm running into a situation where this condition only happens about 90% of the time.
In order to give more context I've created a short screencast demonstrating the problem:
SCREENCAST LINK
For reference purposes, here's the actual SetFieldValue code that is shown in the screencast:
function SetFieldValue ( domObject, value ) {
// as a safety function, check if a string representation of the domObject was passed in and convert it to a jQuery object if it was
if ( jQuery.type(domObject) === "string") {
console.log ("Value passed into SetFieldValue was a string representation so converting to jQuery object");
domObject = jQuery(domObject);
}
if ( jQuery.inArray (domObject.prop('tagName').toLowerCase(),['input' , 'select' , 'textarea']) >= 0 ) {
console.log ("setting to value attribute: " + value);
if ( domObject.hasAttr('id') ) {
domObject.val(value);
//jQuery('#' + domObject.attr('id')).val(value);
} else {
domObject.attr('value',value);
}
console.log ("Using jQuery ID it is set to: " + jQuery('#' + domObject.attr('id')).val() );
console.log ("Using jQuery selector variable it is set to: " + domObject.val() );
} else {
console.log ("setting to html attribute");
domObject.html( value );
}
return domObject;
}
Lets examine the code a bit.
First assigning back to a parameter is not a good practice adding a var at the start of your function would be a lot better, as scope can be lost.
//Suggestion change parameter to domItem
var domObject
Your missing an error handler for when the parameter is not String.
when identifying the type use
<VARNAME>.constructor.toString().match(/function (\w*)/)[1] === "<TYPE>"
It's more efficient and handles custom types.
No need for all the logic in assignment of value attribute. Any dom Object can be made to have a value attribute. also not sure why you are setting the val versus the value.
domObject.attr('value',value);
It is at this point that I can see your code could really use some documentation to help explain purpose
If you are explicitly only wanting to set value on Input fields and set value as innerhtml on non input fields then yes the logic would be needed but could be simplified to ... as the value doesn't need to be detected to overwritten.
if (jQuery.inArray (domObject.prop('tagName').toLowerCase(), ['input' , 'select' , 'textarea']) >= 0) {
domObject.attr('value',value);
} else {
domObject.html( value );
}
No Idea why you are returning the domObject out.
So a quick rewrite without the return and keeping most of the logic adding error handling results in
/*jslint sloppy: true*/
/*global jQuery*/
function SetFieldValue(domString, value) {
// as a safety function, check if a string representation of the domObjects was passed in and convert it to a jQuery object if it was
var domObjects, index;
//errorhandling
if (domString === undefined || domString === null) {
throw {error : "domString must have a value."};
}
if (domString.constructor.toString().match(/function (\w*)/)[1] !== "string") {
if (domString.constructor.toString().match(/function (\w*)/)[1].match(/HTML[a-zA-Z]*Element/) === null) {
throw {error : "domString expected to be String or domObjects"};
}
} else {
if (jQuery(domString).length === 0) {
throw {error : "domString does not resolve to a detectable domObjects."};
}
}
//errorhandling
//action
if (domString.constructor.toString().match(/function (\w*)/)[1].match(/HTML[a-zA-Z]*Element/)) {
//made as an array to normalize as jQuery returns an array allows code to be simplified
domObjects = [domString];
} else {
domObjects = jQuery(domString);
}
//given that domObjects are an array need to step through the array
for (index = domObjects.length - 1; index >= 0; index -= 1) {
if (
jQuery.inArray(
domObjects[index].tagName.toLowerCase(),
['input', 'select', 'textarea']
) >= 0
) {
if (domObjects[index].hasAttr('id')) {
domObjects[index].val(value);
} else {
domObjects[index].attr('value', value);
}
} else {
domObjects[index].html(value);
}
}
}
The above passes JSLint
I know I didn't provide enough context for people to really dig into this problem but I have in the end solved it. What was the issue? Well it was #Kobi who first asked is the DOM element's ID unique ... to which I happily reported it was. And it had been but in fact that WAS the problem. Jesus. It's always the obvious things that you then go onto overlook that get you in trouble.
Anyway, thanks for your patience and help.

CKEditor dialog select box dynamic population

I have two select boxes in a dialog..I was wondering if i can set the contents of the second select box based on the selection of the first box..i.e if select box1 has x selected, then select box2 contents are a, b, c..if select box1 has y selected, then select box2 contents are d, e, f.. Is this possible to implement?
Thanks
I'm guessing that the dialog with two select boxes you mentioned is part of a custom plugin you are creating. So my answer shows code you could use in the dialog file that's being created as part of the plugin.
The onChange function of Selector One creates a reference to Selector Two using:
dialog.getContentElement( 'general', 'selectorTwo' )
'general' is the id of the dialog panel containing the selectors.
'selectorTwo' is the id of the second selector.
I modified the source code from the SimpleLink tutorial hosted at cksource.com:
Creating a CKEditor Plugin with a Custom Dialog Window
http://docs.cksource.com/CKEditor_3.x/Tutorials/SimpleLink_Plugin_Part_1
Download the SimpleLink source code here:
http://docs.cksource.com/images/a/a8/SimpleLink.zip
Unzip and add the simpleLink folder to your ckeditor/plugins directory.
Update your config file by adding the simpleLink plugin to your extraplugins list:
config.extraPlugins = 'onchange,insertsnippet,resconfinearea,resiframe,simpleLink',
Or when you do CKEDITOR.replace use:
extraPlugins : 'onchange,insertsnippet,resconfinearea,resiframe,simpleLink',
Also add the button to your toolbar - 'SimpleLink'
You're going to insert the code snippet from the bottom of this message into the ckeditor/plugins/simpleLink/plugin.js file.
To make the insertion point easy to locate, the first twelve lines of the snippet overlap with lines ( 111 - 122 ) of the simpleLink/plugin.js file.
After you insert the snippet, open ckeditor/_source/plugins/forms/dialogs/select.js.
Copy the following three functions from the top of the select.js file and add them at the top of the simpleLink/plugin.js file:
addOption()
removeAllOptions()
getSelect()
Open a page that's using CKEditor and click on the SimpleLink button you added, you'll see selectors one and two at the bottom of the dialog window. When you change selector one, selector two will be updated. You should be able to modify the code to suit your application.
These pages have some useful information you can refer to when reviewing the code I posted:
Creating a CKEditor Plugin with a Custom Dialog Window
http://docs.cksource.com/CKEditor_3.x/Tutorials/SimpleLink_Plugin_Part_1
Class CKEDITOR.dialog
http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.dialog.html
Class CKEDITOR.dialog.definition
http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.dialog.definition.html
Here's the code snippet to insert into the ckeditor/plugins/simpleLink/plugin.js file.
Just select lines ( 111 - 122 ) and paste this in:
{
type : 'checkbox',
id : 'newPage',
label : 'Opens in a new page',
// Default value.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.ui.dialog.checkbox.html#constructor
'default' : true,
commit : function( data )
{
data.newPage = this.getValue();
}
},
{
type : 'select',
id : 'selectorOne',
label : 'Selector One',
items :
[
[ '<none>', '' ],
[ 'Set 1', 1],
[ 'Set 2', 2 ],
[ 'Set 3', 3 ]
],
onChange : function()
{
var dialog = this.getDialog(),
values = dialog.getContentElement( 'general', 'selectorTwo' ), // 'general' is the id of the dialog panel.
selectedSet = parseInt(this.getValue());
switch(selectedSet)
{
case 1:
optionsNames = new Array("Set One <none>","Set One A","Set One B","Set One C"),
optionsValues = new Array("","setOneA","setOneB","setOneC");
break;
case 2:
optionsNames = new Array("Set Two <none>","Set Two A","Set Two B","Set Two C"),
optionsValues = new Array("","setTwoA","setTwoB","setTwoC");
break;
case 3:
optionsNames = new Array("Set Three <none>","Set Three A","Set Three B","Set Three C"),
optionsValues = new Array("","setThreeA","setThreeB","setThreeC");
break;
default:
optionsNames = new Array("<none>"),
optionsValues = new Array("");
}
removeAllOptions( values );
for ( var i = 0 ; i < optionsNames.length ; i++ )
{
var oOption = addOption( values, optionsNames[ i ],
optionsValues[ i ], dialog.getParentEditor().document );
if ( i == 0 )
{
oOption.setAttribute( 'selected', 'selected' );
oOption.selected = true;
}
}
},
commit : function( data )
{
data.selectorOne = this.getValue();
}
},
{
type : 'select',
id : 'selectorTwo',
label : 'Selector Two',
items :
[
[ '<none>', '' ]
],
commit : function( data )
{
data.selectorTwo = this.getValue();
}
},
Hope this does the trick, let me know if something isn't clear,
Joe
Hi oggiemc,
Good to know that you've had a chance to experiment with the code and it's great that it's been helpful.
Answers to the additional questions about the functions copied from the plugins/forms/dialogs/select.js file.
1) The three functions shouldn't be called when the dialog is opened. That's not occurring in the sample I created as outlined in my initial answer. You might want to search your code for the three function names to see if they are being called someplace other than the ( onChange : function() ) contained in the selectorOne definition.
2) I broke down the ( removeAllOptions() function ) and included it below. It has a similar line of code. For the line you asked about:
if (combo && oOption && oOption.getName() == 'option'),
It says If ( combo exists ) AND ( oOption exists ) AND ( oOption.getName() == 'option' ) the If statement is true.
By "exists", I mean it's not null and not something that evaluates to false (either 0 or a binary Not true).
3) Yes, in this particular usage of the addOption() function, the index variable isn't used. You can remove the two if statements and just leave the code statement from each else clause.
4) obj.getInputElement() returns the DOM element object, but obj.getInputElement().$ only returns the HTML tag for the DOM object. Try adding some console.log calls to the getSelect() function and you'll see the difference:
if ( obj && obj.domId && obj.getInputElement().$ ) { // Dialog element.
console.log(obj.getInputElement().$); // ADDED CONSOLE.LOG
console.log(obj.getInputElement()); // ADDED CONSOLE.LOG
return obj.getInputElement();
Here is the flow for the removeAllOptions() function:
In the onChange() function for selectorOne we create a variable "values" which represents selectorTwo.
values = dialog.getContentElement( 'general', 'selectorTwo' )
Before populating selectorTwo with new option values based on the option selected in selectorOne, we remove any existing options from selectorTwo:
removeAllOptions( values ); // values represents selectorTwo
Within the removeAllOptions() function the variable "combo" is assigned the value that was passed in the function call, so:
combo = values = selectorTwo.
The removeAllOptions() function calls the getSelect() function and sends it the "combo" variable as a parameter.
The "combo" variable will be assigned the value that is returned by the getSelect() function.
Within the getSelect() function the variable "obj" is assigned the value that was passed in the function call, so:
obj = combo = values = selectorTwo.
The getSelect() function can return one of three values:
A) if ( obj && obj.domId && obj.getInputElement().$ )
If obj is true AND if obj.domId is true AND if obj.getInputElement().$ returns anything other than false or null, it will return the value of obj.getInputElement().
So, if the obj variable exists and the domId property of obj is set, it will run the getInputElement() function on obj and check that the DOM property ($) is set.
getInputElement() uses the domId to return the DOM element that corresponds to the selectorTwo element.
B) If not all three parts of A were true, the getSelect() function tries:
if ( obj && obj.$ )
So, if the obj variable exists and it already is the DOM element ($), it will just send back obj.
C) If step B fails, then getSelect() returns false and selectorTwo doesn't have any option elements to remove.
Now back to the removeAllOptions() function to see what happens if step A or B of getSelect() returns something.
In both cases the "combo" variable will be the selectorTwo object with the DOM element (combo.$) set.
It runs a while loop over the array of option elements inside selectorTwo and removes them.
It gets the first option element "getChild( 0 )" and removes it, the option element that was at position Child( 1 ) moves to position Child( 0 ) and the loop continues until there are no option elements left.
The getInputElement() function is located at line ( 1085 ) of ckeditor_source\plugins\dialogui\plugin.js
The getInputElement() function calls the getElement() function.
The getElement() function is located at line ( 2560 ) of ckeditor_source\plugins\dialog\plugin.js
function removeAllOptions( combo )
{
combo = getSelect( combo );
while ( combo.getChild( 0 ) && combo.getChild( 0 ).remove() )
{ /*jsl:pass*/ }
}
function getSelect( obj )
{
if ( obj && obj.domId && obj.getInputElement().$ ) // Dialog element.
return obj.getInputElement();
else if ( obj && obj.$ )
return obj;
return false;
}
Be Well,
Joe
Yes, its totally possible to implement.. In the code, put 2 select boxes.
Then in the js code of that plugin, bind a clickhandler to the select box which puts the value u want in the second select box.

overriding the jquery.param function

I have a problem with the jQuery.param function.
jQuery uses + instead of %20 to URL-encode spaces
var obje = {
'test': 'tester 2'
}
console.log($.param(obje));
returns "test=tester+2"
so I thought about overriding this core function:
(function($){
$.fn.param = function( a, traditional ) {
console.log('custom $.param');
var s = [],
add = function( key, value ) {
// If value is a function, invoke it and return its value
value = jQuery.isFunction( value ) ? value() : value;
s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
};
// Set traditional to true for jQuery <= 1.3.2 behavior.
if ( traditional === undefined ) {
traditional = jQuery.ajaxSettings.traditional;
}
// If an array was passed in, assume that it is an array of form elements.
if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
// Serialize the form elements
jQuery.each( a, function() {
add( this.name, this.value );
} );
} else {
// If traditional, encode the "old" way (the way 1.3.2 or older
// did it), otherwise encode params recursively.
for ( var prefix in a ) {
buildParams( prefix, a[ prefix ], traditional, add );
}
}
return s.join("&");
// Return the resulting serialization
//return s.join( "&" ).replace( r20, "+" );
};
})(jQuery);
var obje = {
'test': 'tester 2'
}
console.log($.param(obje));
This fails however.. The $.param isn't overridden.
Any idea what can be wrong?
Thanks!
Edit: my solution (because I'm a new user I appearently may not answer my own question in 8 hours (Why is that?))
With the solution of ThiefMaster I still had the problem that buildParams is undefined.
I solved this by calling the old function and then replacing the + back to %20
// modification of the jQuery.param function: spaces are encoded by jQuery.param with + instead of %20. replace these back to %20
(function($, oldFunction){
$.param = function( a, traditional ) {
var s = oldFunction.apply(oldFunction,[a,traditional]);
// Return the resulting serialization
return s.replace( '+', '%20' );
};
})(jQuery,jQuery.param);
You need to use $.param instead of $.fn.param (which would be a function to call on a jQuery object, e.g. $(...).param()).
Old post I know, but for the sake of recorded knowledge. To replace the '+' left behind when using $.param(), consider doing the following:
(Using the code you provided)
var obje = {
'test': 'tester 2'
}
console.log($.param(obje).replace(/\+/g, "%20"));
That will result in:
test = tester 2
Hope this helps someone.
The "re-replace" fix may also be implemented by using "beforeSend" in the ajax settings object:
{ beforeSend: function (request, settings) { settings.data = settings.data.replace(/\+/g, "%20"); } }
This approach is suitable for cases where you don't actually want to alter $.param()'s original behavior (for example, if you want "+" in URLs but "%20" for POST data).
[Edited because I remembered that string.replace() will only match once unless it's a regex object with the g flag.]

Categories

Resources