I'm trying to replicate jQuery's element manipulation to a certain extent. Now what I have found to be very useful is the .first() selector. I would like to be able to chain functions like this;
getElement(selector).first().hasClass(className);
Now, there are 2 issues with how far I've gotten (Do note that my code example is minimised, so please, no comments about error-handling.)
var getElement = function(selector, parent) {
ret = document.querySelectorAll(selector);
this.element = ret;
this.hasClass = function(className) {
className.replace('.', '');
if(this.multiple())
{
console.log('Cannot use hasClass function on multiple elements');
return false;
}
};
this.first = function() {
this.element = this.element[0];
return this;
};
return this;
};
My current problem
If I call my function;
var $test = getElement('.something');
//result: nodelist with .something divs
If I call for the first element within the result;
$test.first();
//Result: First div, perfect!
However, now if I call $test again, it will replace the elements property with the result of first(), meaning I have "lost" my old values. I don't want to lose them, I only want the first() functions for that specific functionality. Then I want $test to return all elements again.
Also, recalling first() will now end up undefined, since there is only 1 element left within this as it has deleted the old elements from within the object.
Another attempt
Now, I've also tried to turn it around a bit by returning the first-child instead of the entire class object;
this.first = function() {
return this.element[0];
};
However, I will
$test.first().hasClass(className);
//Returns ERROR, method hasClass undefined
this is because .hasClass exists on the original this, which doesn't get returned anymore since I'm now returning the element.
I have tried to get something out of jQuery's library, though that just confused me more...
I have googled this subject, but with all the 'chaining methods' solutions I'm finding, all of them seem to be overwriting the original values of the object, which is not what I want to happen. One other solution actually required me to re-initiate the object over and over again, which did not seem very efficient to me... Any help is appreciated. I'm assuming I'm going about this completely the wrong way.
-- If you can help me, please do explain why your solution works. I really feel like if I understand this, my understanding of javascript can expand a lot further. I just need to get past this structural(?) issue.
A method like first() should not modify this, it should create a new object and return that. You only use return this; in methods that modify an element rather than returning information derived from the element.
this.first = function() {
return new getElement(this.element[0]);
};
And note that you have to use new getElement to create an object, not just getElement.
This also requires a change to the constructor, so it can accept either a selector string or an element:
var getElement = function(selector, parent) {
var ret = typeof selector == "string" ? document.querySelectorAll(selector) : [selector];
...
}
You should also consider doing this in proper OO fashion, by putting the methods in a prototype, rather than defining them in every object.
var getElement = function(selector, parent) {
var ret = typeof selector == "string" ? document.querySelectorAll(selector) : [selector];
this.element = ret;
};
getElement.prototype.hasClass = function(className) {
className.replace('.', '');
if (this.multiple()) {
console.log('Cannot use hasClass function on multiple elements');
return false;
}
};
getElement.prototype.first = function() {
return new getElement(this.element[0])
};
this in your outer function refers to the window / global object.
Instead, return the ret variable itself.
In the inner functions (which become the object's methods), this acts the way you expect it to.
Here's an alternative solution, which allows chaining, even after you've called the first method:
var getElement = function(selector, parent) {
var ret = typeof selector == 'string' ? document.querySelectorAll(selector)
: selector;
ret.hasClass = function(className) {
if(!this.classList) {
console.log('Cannot use hasClass function on multiple elements');
return false;
} else {
return this.classList.contains(className);
}
};
ret.first = function() {
return new getElement(this[0]);
};
return ret;
};
console.log(getElement('p').length); //2
console.log(getElement('p').first().innerHTML); //abc
console.log(getElement('p').first().hasClass('test')); //true
console.log(getElement('p').first().hasClass('nope')); //fase
console.log(getElement('p').hasClass('test')); //false (multiple elements)
<p class="test">
abc
</p>
<p>
def
</p>
Here is how I would approach this:
Create a constructor, say Search, tasked to find the elements based on the input. Using a constructor is proper OO Programming and you also have the advantage of defining methods once in the prototype and they can be accessed by all instances.
Ensure that the context (this) is an array-like object, with numeric properties and a length, so that you can easily iterate over every matched element in the traditional way (using for loops, [].forEach etc).
Create a function, say getElement, that will use the constructor and return the result without having to use the new keyword all the time. Since the function returns an instance of our constructor, you can chain the methods you want as you would normally do.
The method first uses the constructor to create a new instance instead of modifying the original, since its role is to return the first element, not delete everything but the first element.
Each time you come up with a new method you want your object to have, you can simply add it to the prototype of the constructor.
Snippet:
;(function () {
function Search (value) {
var elements = [];
/* Check whether the value is a string or an HTML element. */
if (typeof value == "string") {
/* Save the selector to the context and use it to get the elements. */
this.selector = value;
elements = document.querySelectorAll(value);
}
else if (value instanceof Element) elements.push(value);
/* Give a length to the context. */
this.length = elements.length;
/* Iterate over every element and inject it to the context. */
for (var i = 0, l = this.length; i < l; i++) this[i] = elements[i];
}
/* The method that returns the first element in a Search instance. */
Object.defineProperty(Search.prototype, "first", {
value: function () {
return new Search(this[0]);
}
});
/* The global function that uses the Search constructor to fetch the elements. */
window.getElement = (value) => new Search(value);
/* Create a reference to the prototype of the constructor for easy access. */
window.getElement.fn = Search.prototype;
})();
/* Get all elements matching the class, the first one, and the first's plain form. */
console.log(getElement(".cls1"));
console.log(getElement(".cls1").first());
console.log(getElement(".cls1").first()[0]);
/* ----- CSS ----- */
.as-console-wrapper {
max-height: 100%!important;
}
<!----- HTML ----->
<div id = "a1" class = "cls1"></div>
<div id = "a2" class = "cls1"></div>
<div id = "a3" class = "cls1"></div>
Example:
In this example, I'm adding a new method called hasClass to the prototype of the constructor.
/* The method that returns whether the first element has a given class. */
Object.defineProperty(getElement.fn, "hasClass", {
value: function (value) {
return this[0].classList.contains(value);
}
});
/* Check whether the first element has the 'cls2' class. */
console.log(getElement(".cls1").first().hasClass("cls2"));
<!----- HTML ----->
<script src="//pastebin.com/raw/e0TM5aYC"></script>
<div id = "a1" class = "cls1 cls2"></div>
<div id = "a2" class = "cls1"></div>
<div id = "a3" class = "cls1"></div>
I think the easiest would be to return a new class that contains the nodes you have selected. That would be the easiest solution, as you don't really want to mutate any of your previous selectors.
I made a small example, using some ES6 that makes a few things easier to work with, which also has a $ to initiate the selections being made.
You would notice that first of all, any selection that is made, is just calling the native document.querySelectorAll but returns a new Node class. Both first and last methods also return those elements.
Lastly, hasClass should work on all elements in the current nodes selections, so it will iterate the current node, and check all classes in there, this one returns a simple bool, so you cannot continue with the method chaining there.
Any method you wish to chain, should either:
return this object (the current node)
return an element of the this object as a new node so any further manipulations can be done there
const $ = (function(global) {
class Node extends Array {
constructor( ...nodes ) {
super();
nodes.forEach( (node, key) => {
this[key] = node;
});
this.length = nodes.length;
}
first() {
return new Node( this[0] );
}
last() {
return new Node( this[this.length-1] );
}
hasClass( ...classes ) {
const set = classes.reduce( (current, cls) => {
current[cls] = true;
return current;
}, {} );
for (let el of this) {
for (let cls of el.classList) {
if (set[cls]) {
return true;
}
}
}
return false;
}
}
global.$ = function( selector ) {
return new Node( ...document.querySelectorAll( selector ) );
};
return global.$;
}(window));
let selector = $('.foo');
let first = selector.first(); // expect 1
console.log(first[0].innerHTML);
let last = selector.last();
console.log(last[0].innerHTML); // expect 4
console.log( first.hasClass('foo') ); // expect true
console.log( first.hasClass('bar') ); // expect false
console.log( selector.hasClass('foo') ); // expect true
console.log( selector.hasClass('bar') ); // expect true
<div class="foo">1</div>
<div class="foo">2</div>
<div class="foo bar">3</div>
<div class="foo">4</div>
You can update getElement so it returns back again when you send it an element.
var getElement = function(selector, parent) {
var ret = null
if (typeof selector === "string") {
ret = document.querySelectorAll(selector);
} else {
ret = selector
}
this.element = ret;
this.hasClass = function(className) {
className.replace('.', '');
if (this.multiple()) {
console.log('Cannot use hasClass function on multiple elements');
return false;
}
};
this.first = function() {
this.element = getElement(this.element[0]);
return this;
};
return this;
};
var test = getElement(".foo");
console.log(test.first())
console.log(test.first().hasClass)
<div class="foo">1</div>
<div class="foo">2</div>
<div class="foo">3</div>
<div class="foo">4</div>
You can use .querySelectorAll(), spread element and Array.prototype.find(), which returns the first match within an array or undefined
const getElement = (selector = "", {prop = "", value = "", first = false} = {}) => {
const el = [...document.querySelectorAll(selector)];
if (first) return el.find(({[prop]:match}) => match && match === value)
else return el;
};
let first = getElement("span", {prop: "className", value: "abc", first: true});
console.log(first);
let last = getElement("span");
console.log(all);
<span class="abc">123</span>
<span class="abc">456</span>
Related
I am writing a micro-library instead of using jQuery. I need only 3-4 methods ( for DOM traversal, Adding Eventlisteners etc). So I decided to write them myself instead of bloating the site with jQuery.
Here is the snippet from the code:
lib.js
window.twentyFourJS = (function() {
let elements;
const Constructor = function(selector) {
elements = document.querySelectorAll(selector);
this.elements = elements;
};
Constructor.prototype.addClass = function(className) {
elements.forEach( item => item.classList.add(className));
return this;
};
Constructor.prototype.on = function(event, callback, useCapture = false){
elements.forEach((element) => {
element.addEventListener(event, callback, useCapture);
});
return this;
}
const initFunction = function(selector){
return new Constructor(selector);
}
return initFunction;
})(twentyFourJS);
script.js
(function($){
$('.tab-menu li a').on('click', function(event) {
console.log('I am clicked'); // This works
this.addClass('MyClass'); // This does NOT work (as expected)
// I want to be able to do this
$(this).addClass('MyClass');
event.preventDefault();
});
})(twentyFourJS);
Basically I want to be able to use $(this) like we use it in jQuery.
this.addClass('MyClass') and $(this).addClass('MyClass') won't work and this is the expected behaviour.
As per my understanding this is referring to the plain HTML element. So it does not have access to any Constructor methods. it won't work.
And I have not written any code that will wrap element in Constructor object in $(this). I will have to do some changes to my Constructor so that I can access the Constructor functions using $(this). What are those changes/addition to it?
Kindly recommend only Vanilla JS ways instead of libraries.
in your constructor you need to see what you have and handle it in different ways.
const Constructor = function(selector) {
if (typeof selector === 'string') {
elements = document.querySelectorAll(selector);
} else {
// need some sort of check to see if collection or single element
// This could be improved since it could fail when someone would add a length property/attribute
elements = selector.length ? selector : [selector];
}
this.elements = elements;
};
All you really need to do is make sure your Constructor argument can distinguish between a string selector being passed in, and an object.
const Constructor = function(selector) {
if(typeof selector == "string"){
elements = document.querySelectorAll(selector);
this.elements = elements;
}
else{
this.elements = selector;
}
};
You can go further than this, but at a very minimum for the example given that works.
Live example below:
window.twentyFourJS = (function() {
let elements;
const Constructor = function(selector) {
if(typeof selector == "string"){
elements = document.querySelectorAll(selector);
this.elements = elements;
}
else{
this.elements = selector;
}
};
Constructor.prototype.addClass = function(className) {
elements.forEach( item => item.classList.add(className));
return this;
};
Constructor.prototype.on = function(event, callback, useCapture = false){
elements.forEach((element) => {
element.addEventListener(event, callback, useCapture);
});
return this;
}
const initFunction = function(selector){
return new Constructor(selector);
}
return initFunction;
})();
(function($){
$('.btn').on('click', function(event) {
console.log('I am clicked'); // This works
// I want to be able to do this
$(this).addClass('MyClass');
event.preventDefault();
});
})(twentyFourJS);
.MyClass{
background-color:red
}
<button class="btn">Click me</btn>
first You Need to Check for a string
case 1. $("div")
Then You need to Check for it's NodeType and for a window
case 1. var elm = document .getElementById("ID")
$(elm)
case 2. $(this) -- window
function $(selector){
var element;
if (typeof selector === 'string') {
element = document.querySelectorAll(selector)
}
if (element.nodeType || element=== window) element= [selector];
return element ;
}
I need to do some experiment and I need to know some kind of unique identifier for objects in javascript, so I can see if they are the same. I don't want to use equality operators, I need something like the id() function in python.
Does something like this exist ?
Update My original answer below was written 6 years ago in a style befitting the times and my understanding. In response to some conversation in the comments, a more modern approach to this is as follows:
(function() {
if ( typeof Object.id != "undefined" ) return;
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
return o.__uniqueid;
};
})();
var obj = { a: 1, b: 1 };
console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
console.log(Object.id(function() {}));
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`
If you have archaic browser requirements, check here for browser compatibility for Object.defineProperty.
The original answer is kept below (instead of just in the change history) because I think the comparison is valuable.
You can give the following a spin. This also gives you the option to explicitly set an object's ID in its constructor or elsewhere.
(function() {
if ( typeof Object.prototype.uniqueId == "undefined" ) {
var id = 0;
Object.prototype.uniqueId = function() {
if ( typeof this.__uniqueid == "undefined" ) {
this.__uniqueid = ++id;
}
return this.__uniqueid;
};
}
})();
var obj1 = {};
var obj2 = new Object();
console.log(obj1.uniqueId());
console.log(obj2.uniqueId());
console.log([].uniqueId());
console.log({}.uniqueId());
console.log(/./.uniqueId());
console.log((function() {}).uniqueId());
Take care to make sure that whatever member you use to internally store the unique ID doesn't collide with another automatically created member name.
So far as my observation goes, any answer posted here can have unexpected side effects.
In ES2015-compatible enviroment, you can avoid any side effects by using WeakMap.
const id = (() => {
let currentId = 0;
const map = new WeakMap();
return (object) => {
if (!map.has(object)) {
map.set(object, ++currentId);
}
return map.get(object);
};
})();
id({}); //=> 1
Latest browsers provide a cleaner method for extending Object.prototype. This code will make the property hidden from property enumeration (for p in o)
For the browsers that implement defineProperty, you can implement uniqueId property like this:
(function() {
var id_counter = 1;
Object.defineProperty(Object.prototype, "__uniqueId", {
writable: true
});
Object.defineProperty(Object.prototype, "uniqueId", {
get: function() {
if (this.__uniqueId == undefined)
this.__uniqueId = id_counter++;
return this.__uniqueId;
}
});
}());
For details, see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
Actually, you don't need to modify the object prototype and add a function there. The following should work well for your purpose.
var __next_objid=1;
function objectId(obj) {
if (obj==null) return null;
if (obj.__obj_id==null) obj.__obj_id=__next_objid++;
return obj.__obj_id;
}
For browsers implementing the Object.defineProperty() method, the code below generates and returns a function that you can bind to any object you own.
This approach has the advantage of not extending Object.prototype.
The code works by checking if the given object has a __objectID__ property, and by defining it as a hidden (non-enumerable) read-only property if not.
So it is safe against any attempt to change or redefine the read-only obj.__objectID__ property after it has been defined, and consistently throws a nice error instead of silently fail.
Finally, in the quite extreme case where some other code would already have defined __objectID__ on a given object, this value would simply be returned.
var getObjectID = (function () {
var id = 0; // Private ID counter
return function (obj) {
if(obj.hasOwnProperty("__objectID__")) {
return obj.__objectID__;
} else {
++id;
Object.defineProperty(obj, "__objectID__", {
/*
* Explicitly sets these two attribute values to false,
* although they are false by default.
*/
"configurable" : false,
"enumerable" : false,
/*
* This closure guarantees that different objects
* will not share the same id variable.
*/
"get" : (function (__objectID__) {
return function () { return __objectID__; };
})(id),
"set" : function () {
throw new Error("Sorry, but 'obj.__objectID__' is read-only!");
}
});
return obj.__objectID__;
}
};
})();
Typescript version of #justin answer, ES6 compatible, using Symbols to prevent any key collision and added into the global Object.id for convenience. Just copy paste the code below, or put it into an ObjecId.ts file you will import.
(enableObjectID)();
declare global {
interface ObjectConstructor {
id: (object: any) => number;
}
}
const uniqueId: symbol = Symbol('The unique id of an object');
export function enableObjectID(): void {
if (typeof Object['id'] !== 'undefined') {
return;
}
let id: number = 0;
Object['id'] = (object: any) => {
const hasUniqueId: boolean = !!object[uniqueId];
if (!hasUniqueId) {
object[uniqueId] = ++id;
}
return object[uniqueId];
};
}
Example of usage:
console.log(Object.id(myObject));
jQuery code uses it's own data() method as such id.
var id = $.data(object);
At the backstage method data creates a very special field in object called "jQuery" + now() put there next id of a stream of unique ids like
id = elem[ expando ] = ++uuid;
I'd suggest you use the same method as John Resig obviously knows all there is about JavaScript and his method is based on all that knowledge.
For the purpose of comparing two objects, the simplest way to do this would be to add a unique property to one of the objects at the time you need to compare the objects, check if the property exists in the other and then remove it again. This saves overriding prototypes.
function isSameObject(objectA, objectB) {
unique_ref = "unique_id_" + performance.now();
objectA[unique_ref] = true;
isSame = objectB.hasOwnProperty(unique_ref);
delete objectA[unique_ref];
return isSame;
}
object1 = {something:true};
object2 = {something:true};
object3 = object1;
console.log(isSameObject(object1, object2)); //false
console.log(isSameObject(object1, object3)); //true
I faced the same problem and here's the solution I implemented with ES6
code
let id = 0; // This is a kind of global variable accessible for every instance
class Animal {
constructor(name){
this.name = name;
this.id = id++;
}
foo(){}
// Executes some cool stuff
}
cat = new Animal("Catty");
console.log(cat.id) // 1
I've used code like this, which will cause Objects to stringify with unique strings:
Object.prototype.__defineGetter__('__id__', function () {
var gid = 0;
return function(){
var id = gid++;
this.__proto__ = {
__proto__: this.__proto__,
get __id__(){ return id }
};
return id;
}
}.call() );
Object.prototype.toString = function () {
return '[Object ' + this.__id__ + ']';
};
the __proto__ bits are to keep the __id__ getter from showing up in the object. this has been only tested in firefox.
Notwithstanding the advice not to modify Object.prototype, this can still be really useful for testing, within a limited scope. The author of the accepted answer changed it, but is still setting Object.id, which doesn't make sense to me. Here's a snippet that does the job:
// Generates a unique, read-only id for an object.
// The _uid is generated for the object the first time it's accessed.
(function() {
var id = 0;
Object.defineProperty(Object.prototype, '_uid', {
// The prototype getter sets up a property on the instance. Because
// the new instance-prop masks this one, we know this will only ever
// be called at most once for any given object.
get: function () {
Object.defineProperty(this, '_uid', {
value: id++,
writable: false,
enumerable: false,
});
return this._uid;
},
enumerable: false,
});
})();
function assert(p) { if (!p) throw Error('Not!'); }
var obj = {};
assert(obj._uid == 0);
assert({}._uid == 1);
assert([]._uid == 2);
assert(obj._uid == 0); // still
This one will calculate a HashCode for each object, optimized for string, number and virtually anything that has a getHashCode function. For the rest it assigns a new reference number.
(function() {
var __gRefID = 0;
window.getHashCode = function(ref)
{
if (ref == null) { throw Error("Unable to calculate HashCode on a null reference"); }
// already cached reference id
if (ref.hasOwnProperty("__refID")) { return ref["__refID"]; }
// numbers are already hashcodes
if (typeof ref === "number") { return ref; }
// strings are immutable, so we need to calculate this every time
if (typeof ref === "string")
{
var hash = 0, i, chr;
for (i = 0; i < ref.length; i++) {
chr = ref.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
return hash;
}
// virtual call
if (typeof ref.getHashCode === "function") { return ref.getHashCode(); }
// generate and return a new reference id
return (ref["__refID"] = "ref" + __gRefID++);
}
})();
If you came here because you deal with class instances like me you can use static vars/methods to reference instances by a custom unique id:
class Person {
constructor( name ) {
this.name = name;
this.id = Person.ix++;
Person.stack[ this.id ] = this;
}
}
Person.ix = 0;
Person.stack = {};
Person.byId = id => Person.stack[ id ];
let store = {};
store[ new Person( "joe" ).id ] = true;
store[ new Person( "tim" ).id ] = true;
for( let id in store ) {
console.log( Person.byId( id ).name );
}
Here's a variant of Justin Johnson's answer that provides a scalability benefit when you are creating billions of objects for which you want the ID.
Specifically, rather than solely using a 1-up counter (that might overflow the representational limits of Number, and can't be cycled without risking reusing an ID), we register the object and its newly generated ID with a FinalizationRegistry, such that, at some point after the object is garbage collected, the ID is returned to a freelist for reuse by a newly created object (Python's id function can also return the same ID for multiple objects, so long as the existence of the two objects does not overlap in time).
Limitations:
It only works on objects, not JS primitives (this is somewhat reasonable; unlike Python, where everything is an object, JS primitives typically aren't, and the id function logically only works on objects, since primitives need not "exist" in any reasonably identifiable way).
If the code creates (without discarding) billions of objects, asks for their IDs, then releases them all at once and never asks for an ID again, the recovered IDs in the freelist constitute a memory leak of sorts. Hopefully the JS optimizer stores them efficiently, so the cost remains a small fraction of what the objects themselves cost, but it's still a cost. In cases where objects with IDs are regularly created and destroyed, the wasted memory is roughly tied to the maximum number of such ID-ed objects in existence at any given point in time.
If those limitations aren't a problem though, this works fairly well. I modified the testing code a bit to hand control back to the event loop (and hopefully the garbage collector) now and again while creating 10M garbage objects to ID, and on my browser, nearly half the object IDs get reclaimed for reuse; the final loop making five objects and IDing them produces IDs just above 1M, when over 2M objects had IDs generated at some point. In a realistic scenario with meaningful code executing and real async usage I'd expect better results simply because there would be more opportunities for the finalization registry to perform cleanup.
async function sleep(ms) {
await _sleep(ms);
}
function _sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
(function() {
if ( typeof Object.id != "undefined" ) return;
var freelist = []; // Stores previously used IDs for reuse when an object with
// an ID is garbage collected, so creating and dropping billions
// of objects doesn't consume all available IDs
const registry = new FinalizationRegistry((freeid) => {
freelist.push(freeid);
});
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: freelist.length ? freelist.pop() : ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
registry.register(o, o.__uniqueid); // Sometime after o is collected, its ID
// will be reclaimed for use by a new object
return o.__uniqueid;
};
})();
var obj = { a: 1, b: 1 };
console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
var idsum = 0; // So we do something real to prevent optimizing out code
// Make a ton of temporary objects with IDs, handing control back to the event loop
// every once in a while to (hopefully) see some IDs returned to the pool
for (var i = 0; i < 1000000; ++i) {
idsum += Object.id({c: i});
}
sleep(10).then(() => {
console.log(Object.id(function() { console.log("Hey"); }));
for (var i = 1000000; i < 2000000; ++i) {
idsum += Object.id({c: i});
}
console.log(Object.id(function() { console.log("There"); }));
sleep(10).then(() => {
for (var i = 0; i < 5; ++i) {
console.log(Object.id([i]));
}
console.log(idsum);
});
});
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`
How Jquery can chain and return multiple values?
I know you can chain:
const $ = {
a() {
return this;
}
b() {
return this;
}
}
$.a().b()
The example below will speaks by itself:
$('div').find('p').hide().css() // find an apply style
$('div').find('p') // return all the "p"
See my example
How jQuery return all the p and keep the plugin instance?
How can I achieve the same behavior?
How to know if there is another call after find()?
Thanks.
All of these API members are returning a jQuery object. It just so happens that the jQuery API has all of these members on it. That is how you are able to chain calls on the same object.
Taking a look at the docs:
In API calls that return jQuery, the value returned will be the original jQuery object unless otherwise documented by that AP
show() returns a jQuery type
hide() returns a jQuery type
find() returns a jQuery type
Note that css() doesn't return a jQuery type, so you can't chain off that.
jQuery follows an approach like this one:
function $(param) {
if(!(this instanceof $)) // if $ is called without using 'new'
return new $(param); // then return a new instance
this.param = param; // ...
}
$.prototype.a = function() { // the function a has to...
// a's logic
console.log("this is a");
return this; // return this so there will be chaining
}
$.prototype.b = function() { // the same goes for b
// b's logic
console.log("this is b");
return this;
}
$.c = function() {
// c's logic
console.log(c);
// not returning this as c can't be chained
}
$("some param").a().b().a();
In the example, a and b are methods (attached to the prototype) that return this so they can be chained (example of $(...).val(), $(...).addClass()). c however is attached as a property to the function object $ directly, thus it can't be chained (example of $.isArray() and $.each() not to be confused with $(...).each()).
A mini-jQuery example:
function $(selector) {
if(!(this instanceof $))
return new $(selector);
if(selector instanceof $) // if selector is already an instance of $ then just return it to be used as it is
return selector;
else if(typeof selector === "string") // otherwise, if selector is a string, then select the elements
this.elems = document.querySelectorAll(selector);
else if($.isArray(selector)) // otherwise, if the selector is an array, then the elements will be those in the array ( this is used by find )
this.elems = selector;
else if(!selector) // otherwise, selector is a DOM element
this.elems = [selector];
}
$.prototype.each = function(callback) { // take a function (callback) and call it on all elements
for(var i = 0; i < this.elems.length; i++) // for each element in this.elems
callback.call(this.elems[i], i, this.elems[i]); // call callback, setting its this to the current element and passing to it two parameters: the index and the element itself
return this;
}
$.prototype.css = function(prop, value) { // a function that take a CSS property and probably a value and either set or return the value of that CSS property
if(value === undefined) // if the value is not provided, then
return this.elems[0].style[prop]; // return the value of the first element in the selection (some checking if the element exist should be here)
return this.each(function() { // if a value is provided, then
this.style[prop] = value; // set the style of each element in the selection, and then return this (this.each returns this so just return this.each())
});
}
$.prototype.text = function(val) { // follows the same as css above
if(val === undefined)
return this.elems[0].textContent;
return this.each(function() {
this.textContent = val;
});
}
$.isArray = function(obj) { // is not a method (this is not the instance of the class)
return Object.prototype.toString.call(obj) === "[object Array]";
}
// FIND:
$.prototype.find = function(selector) {
var newElems = []; // the array of new elements
this.each(function() { // for each element in the current selection
var thisElems = this.querySelectorAll(selector); // select it descendants that match the selector
for(var i = 0; i < thisElems.length; i++) // add them to the result array
newElems.push(thisElems[i]);
});
// remove duplicates // this is important as elements could be selected twice (elements inside a parent inside a grand parent where both parent and grand parent are in the old selection), check jQuery's source code for how they remove duplicats using sort
return $(newElems); // return a new $ object using the new array of elements as the wrapped elements (check the constructor for more informations)
}
// usage:
$("div").css("background", "#fd8") // selects all divs and change their background
.find("span") // now find all spans inside those divs
.css("background", "#0ff"); // change their background
<div>a</div>
<div class="even"><span>b</span></div>
<div>c</div>
<div class="even">d</div>
<div><span>e</span></div>
<span>outer span</span>
I am relatively new to OOP so I am not sure about the terminology.
I have created a DOM element as a field of an object (eg myObject.myElement) and I appended the element to the document. The object has a .mousemove() event attached (using jQuery). I want to be able to select the object (myObject) for whom the selected element (myElement) is a field so that I can access other fields of the object. There is more than one object and the event handler is the same for elements of different objects. Is it possible to select the parent object of the element? Do I need to give the element the same fields so that I can access the same data?
I want to be able to do something like this but maybe it is not that straightforward:
$('.bubble').on({mousemove: function () {
parentObject = this.parentObject();
alert(parentObject.otherDataField);
});
The element was created like this:
function bubbleObject(value)
{
this.value = value;
this.element = document.createElement('div');
$(this.element).appendTo('.bubbles');
}
myFirstBubble = new bubbleObject(10);
mySecondBubble = new bubbleObject(100);
and I need to be able to access the value field for the object (I am making it simpler as the code is pretty long and mostly irrelevant to my issue).
Do I need to do this:
function bubbleObject(value)
{
this.value = value;
this.element = document.createElement('div');
$(this.element).appendTo('.bubbles');
this.element.value = value; // add same value to element
}
or is there a better way?
A back reference to the parent could be better than copying values:
function bubbleObject(value)
{
this.value = value;
this.element = document.createElement('div');
$(this.element).appendTo('.bubbles');
this.element.parent = this;
}
However, using this way you have to use element.parent.value instead.
Another approach (as I mentioned before) is to scan all element-containing objects if they contain a specific element. If you don't have all these objects in an array you have to scan all window components (as I said, this is expensive):
function findElement(element) {
for (obj in window) {
if (typeof obj === 'object' && obj.myElement == element) {
return obj;
}
}
return null;
}
This is just an untested draft. And it would be so much better if you have a list of parent objects which you can use instead of window.
function findElement(element) {
for (int i = 0, l = objList.length; i < l; i++) {
if (typeof objList[i] === 'object' && objList[i].myElement == element) {
return objList[i];
}
}
return null;
}
I need to do some experiment and I need to know some kind of unique identifier for objects in javascript, so I can see if they are the same. I don't want to use equality operators, I need something like the id() function in python.
Does something like this exist ?
Update My original answer below was written 6 years ago in a style befitting the times and my understanding. In response to some conversation in the comments, a more modern approach to this is as follows:
(function() {
if ( typeof Object.id != "undefined" ) return;
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
return o.__uniqueid;
};
})();
var obj = { a: 1, b: 1 };
console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
console.log(Object.id(function() {}));
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`
If you have archaic browser requirements, check here for browser compatibility for Object.defineProperty.
The original answer is kept below (instead of just in the change history) because I think the comparison is valuable.
You can give the following a spin. This also gives you the option to explicitly set an object's ID in its constructor or elsewhere.
(function() {
if ( typeof Object.prototype.uniqueId == "undefined" ) {
var id = 0;
Object.prototype.uniqueId = function() {
if ( typeof this.__uniqueid == "undefined" ) {
this.__uniqueid = ++id;
}
return this.__uniqueid;
};
}
})();
var obj1 = {};
var obj2 = new Object();
console.log(obj1.uniqueId());
console.log(obj2.uniqueId());
console.log([].uniqueId());
console.log({}.uniqueId());
console.log(/./.uniqueId());
console.log((function() {}).uniqueId());
Take care to make sure that whatever member you use to internally store the unique ID doesn't collide with another automatically created member name.
So far as my observation goes, any answer posted here can have unexpected side effects.
In ES2015-compatible enviroment, you can avoid any side effects by using WeakMap.
const id = (() => {
let currentId = 0;
const map = new WeakMap();
return (object) => {
if (!map.has(object)) {
map.set(object, ++currentId);
}
return map.get(object);
};
})();
id({}); //=> 1
Latest browsers provide a cleaner method for extending Object.prototype. This code will make the property hidden from property enumeration (for p in o)
For the browsers that implement defineProperty, you can implement uniqueId property like this:
(function() {
var id_counter = 1;
Object.defineProperty(Object.prototype, "__uniqueId", {
writable: true
});
Object.defineProperty(Object.prototype, "uniqueId", {
get: function() {
if (this.__uniqueId == undefined)
this.__uniqueId = id_counter++;
return this.__uniqueId;
}
});
}());
For details, see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
Actually, you don't need to modify the object prototype and add a function there. The following should work well for your purpose.
var __next_objid=1;
function objectId(obj) {
if (obj==null) return null;
if (obj.__obj_id==null) obj.__obj_id=__next_objid++;
return obj.__obj_id;
}
For browsers implementing the Object.defineProperty() method, the code below generates and returns a function that you can bind to any object you own.
This approach has the advantage of not extending Object.prototype.
The code works by checking if the given object has a __objectID__ property, and by defining it as a hidden (non-enumerable) read-only property if not.
So it is safe against any attempt to change or redefine the read-only obj.__objectID__ property after it has been defined, and consistently throws a nice error instead of silently fail.
Finally, in the quite extreme case where some other code would already have defined __objectID__ on a given object, this value would simply be returned.
var getObjectID = (function () {
var id = 0; // Private ID counter
return function (obj) {
if(obj.hasOwnProperty("__objectID__")) {
return obj.__objectID__;
} else {
++id;
Object.defineProperty(obj, "__objectID__", {
/*
* Explicitly sets these two attribute values to false,
* although they are false by default.
*/
"configurable" : false,
"enumerable" : false,
/*
* This closure guarantees that different objects
* will not share the same id variable.
*/
"get" : (function (__objectID__) {
return function () { return __objectID__; };
})(id),
"set" : function () {
throw new Error("Sorry, but 'obj.__objectID__' is read-only!");
}
});
return obj.__objectID__;
}
};
})();
Typescript version of #justin answer, ES6 compatible, using Symbols to prevent any key collision and added into the global Object.id for convenience. Just copy paste the code below, or put it into an ObjecId.ts file you will import.
(enableObjectID)();
declare global {
interface ObjectConstructor {
id: (object: any) => number;
}
}
const uniqueId: symbol = Symbol('The unique id of an object');
export function enableObjectID(): void {
if (typeof Object['id'] !== 'undefined') {
return;
}
let id: number = 0;
Object['id'] = (object: any) => {
const hasUniqueId: boolean = !!object[uniqueId];
if (!hasUniqueId) {
object[uniqueId] = ++id;
}
return object[uniqueId];
};
}
Example of usage:
console.log(Object.id(myObject));
jQuery code uses it's own data() method as such id.
var id = $.data(object);
At the backstage method data creates a very special field in object called "jQuery" + now() put there next id of a stream of unique ids like
id = elem[ expando ] = ++uuid;
I'd suggest you use the same method as John Resig obviously knows all there is about JavaScript and his method is based on all that knowledge.
For the purpose of comparing two objects, the simplest way to do this would be to add a unique property to one of the objects at the time you need to compare the objects, check if the property exists in the other and then remove it again. This saves overriding prototypes.
function isSameObject(objectA, objectB) {
unique_ref = "unique_id_" + performance.now();
objectA[unique_ref] = true;
isSame = objectB.hasOwnProperty(unique_ref);
delete objectA[unique_ref];
return isSame;
}
object1 = {something:true};
object2 = {something:true};
object3 = object1;
console.log(isSameObject(object1, object2)); //false
console.log(isSameObject(object1, object3)); //true
I faced the same problem and here's the solution I implemented with ES6
code
let id = 0; // This is a kind of global variable accessible for every instance
class Animal {
constructor(name){
this.name = name;
this.id = id++;
}
foo(){}
// Executes some cool stuff
}
cat = new Animal("Catty");
console.log(cat.id) // 1
I've used code like this, which will cause Objects to stringify with unique strings:
Object.prototype.__defineGetter__('__id__', function () {
var gid = 0;
return function(){
var id = gid++;
this.__proto__ = {
__proto__: this.__proto__,
get __id__(){ return id }
};
return id;
}
}.call() );
Object.prototype.toString = function () {
return '[Object ' + this.__id__ + ']';
};
the __proto__ bits are to keep the __id__ getter from showing up in the object. this has been only tested in firefox.
Notwithstanding the advice not to modify Object.prototype, this can still be really useful for testing, within a limited scope. The author of the accepted answer changed it, but is still setting Object.id, which doesn't make sense to me. Here's a snippet that does the job:
// Generates a unique, read-only id for an object.
// The _uid is generated for the object the first time it's accessed.
(function() {
var id = 0;
Object.defineProperty(Object.prototype, '_uid', {
// The prototype getter sets up a property on the instance. Because
// the new instance-prop masks this one, we know this will only ever
// be called at most once for any given object.
get: function () {
Object.defineProperty(this, '_uid', {
value: id++,
writable: false,
enumerable: false,
});
return this._uid;
},
enumerable: false,
});
})();
function assert(p) { if (!p) throw Error('Not!'); }
var obj = {};
assert(obj._uid == 0);
assert({}._uid == 1);
assert([]._uid == 2);
assert(obj._uid == 0); // still
This one will calculate a HashCode for each object, optimized for string, number and virtually anything that has a getHashCode function. For the rest it assigns a new reference number.
(function() {
var __gRefID = 0;
window.getHashCode = function(ref)
{
if (ref == null) { throw Error("Unable to calculate HashCode on a null reference"); }
// already cached reference id
if (ref.hasOwnProperty("__refID")) { return ref["__refID"]; }
// numbers are already hashcodes
if (typeof ref === "number") { return ref; }
// strings are immutable, so we need to calculate this every time
if (typeof ref === "string")
{
var hash = 0, i, chr;
for (i = 0; i < ref.length; i++) {
chr = ref.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
return hash;
}
// virtual call
if (typeof ref.getHashCode === "function") { return ref.getHashCode(); }
// generate and return a new reference id
return (ref["__refID"] = "ref" + __gRefID++);
}
})();
If you came here because you deal with class instances like me you can use static vars/methods to reference instances by a custom unique id:
class Person {
constructor( name ) {
this.name = name;
this.id = Person.ix++;
Person.stack[ this.id ] = this;
}
}
Person.ix = 0;
Person.stack = {};
Person.byId = id => Person.stack[ id ];
let store = {};
store[ new Person( "joe" ).id ] = true;
store[ new Person( "tim" ).id ] = true;
for( let id in store ) {
console.log( Person.byId( id ).name );
}
Here's a variant of Justin Johnson's answer that provides a scalability benefit when you are creating billions of objects for which you want the ID.
Specifically, rather than solely using a 1-up counter (that might overflow the representational limits of Number, and can't be cycled without risking reusing an ID), we register the object and its newly generated ID with a FinalizationRegistry, such that, at some point after the object is garbage collected, the ID is returned to a freelist for reuse by a newly created object (Python's id function can also return the same ID for multiple objects, so long as the existence of the two objects does not overlap in time).
Limitations:
It only works on objects, not JS primitives (this is somewhat reasonable; unlike Python, where everything is an object, JS primitives typically aren't, and the id function logically only works on objects, since primitives need not "exist" in any reasonably identifiable way).
If the code creates (without discarding) billions of objects, asks for their IDs, then releases them all at once and never asks for an ID again, the recovered IDs in the freelist constitute a memory leak of sorts. Hopefully the JS optimizer stores them efficiently, so the cost remains a small fraction of what the objects themselves cost, but it's still a cost. In cases where objects with IDs are regularly created and destroyed, the wasted memory is roughly tied to the maximum number of such ID-ed objects in existence at any given point in time.
If those limitations aren't a problem though, this works fairly well. I modified the testing code a bit to hand control back to the event loop (and hopefully the garbage collector) now and again while creating 10M garbage objects to ID, and on my browser, nearly half the object IDs get reclaimed for reuse; the final loop making five objects and IDing them produces IDs just above 1M, when over 2M objects had IDs generated at some point. In a realistic scenario with meaningful code executing and real async usage I'd expect better results simply because there would be more opportunities for the finalization registry to perform cleanup.
async function sleep(ms) {
await _sleep(ms);
}
function _sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
(function() {
if ( typeof Object.id != "undefined" ) return;
var freelist = []; // Stores previously used IDs for reuse when an object with
// an ID is garbage collected, so creating and dropping billions
// of objects doesn't consume all available IDs
const registry = new FinalizationRegistry((freeid) => {
freelist.push(freeid);
});
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: freelist.length ? freelist.pop() : ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
registry.register(o, o.__uniqueid); // Sometime after o is collected, its ID
// will be reclaimed for use by a new object
return o.__uniqueid;
};
})();
var obj = { a: 1, b: 1 };
console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
var idsum = 0; // So we do something real to prevent optimizing out code
// Make a ton of temporary objects with IDs, handing control back to the event loop
// every once in a while to (hopefully) see some IDs returned to the pool
for (var i = 0; i < 1000000; ++i) {
idsum += Object.id({c: i});
}
sleep(10).then(() => {
console.log(Object.id(function() { console.log("Hey"); }));
for (var i = 1000000; i < 2000000; ++i) {
idsum += Object.id({c: i});
}
console.log(Object.id(function() { console.log("There"); }));
sleep(10).then(() => {
for (var i = 0; i < 5; ++i) {
console.log(Object.id([i]));
}
console.log(idsum);
});
});
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`