14

Is there any way to have DOM elements selectable through objects?

For example I want to be able to associate objects to DOM elements like so:

var obj = { a: 1, b:2 };
$('a').click(function() { this.selectThing = obj });

And later on...

$.something(obj);

Or even better:

$('a|selectThing?=', obj);

Something like that. You can see that I want to associate an object to a DOM element in such a way that I can grab the element with the object.

I know this can be done with the filter() method, my question is if there's a more elegant way that doesn't use filter() to do this.

EDIT:

To clarify, I want to be able to use an object kind of like a selector, so I can do something similar to this $(obj) obviously that won't work, but you get the idea (I hope)

EDIT #2:

I want to be able to do something like this:

var obj = { prop: 'prop' };
$('a').bindTo(obj);
$.retreive(obj) // should equal $('a')

I don't want it to alter obj in any way though (obj should still be {prop: 'prop'} only).

qwertymk
  • 34,200
  • 28
  • 121
  • 184
  • Can you add an example of using the filter method to achieve this? I have no idea what you are trying to do otherwise. – Esailija May 03 '12 at 12:28
  • Did you check the answer I suggested http://stackoverflow.com/questions/2891452/jquery-data-selector/2895933#2895933 ? – Meligy May 03 '12 at 22:39
  • @MohamedMeligy: yes, but once again that isn't using an object in the selector, it's selecting with data associated with the DOM element instead. – qwertymk May 03 '12 at 23:49
  • I guess I'm wondering why the "obj" must not be altered. To me it would seem to make more sense to create a prototype of "obj" with a bindTo() function that takes a jQuery object or CSS selector. Then store any data in obj or obj.data and use obj.bindTo(...) and $.retrieve(obj) where retrieve checks some data member of "obj" referencing the DOM element. If you do $('a').bindTo(obj), then wouldn't you need another object associating the binds (since 'obj' has no idea what its bound to)? What exactly would $.retieve() check? Does it lookup "obj" s/where 2 find the element? Or would it ask "obj"? – Kasapo May 08 '12 at 18:17

7 Answers7

8

demo

var $div1 = $('.box1');
var $div2 = $('.box2');
var obj = { a: $div1, b: $div2 };


obj.a.css({background:'red'});


Or the short way: var obj = { a: $('.box1'), b: $('.box2') };

demo jsBin 2

var obj = $('.box1, .box2'); // store objects
obj.css({background:'red'}); // access collection
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • @tobyodavies: I'm looking for a way to use an object to get an element, not save elements on an object. What I want is similar to having a selector that can be an object – qwertymk May 02 '12 at 04:03
  • Isn't this solution exactly that? The object isn't really "saving" elements within itself, rather the object contains 2 jquery objects which reference DOM elements. If you want to do $(obj) then obj must be a string with a jquery selector. I guess what is the nature of the "obj" object? You could always override the "toString" function of the object to print out a selector I guess... I'm still not sure what you're trying to do. Perhaps a little elaboration is in order? – Kasapo May 08 '12 at 16:11
  • @Kasapo: See edit on question, I don't need to pass an object into the main jQuery function, which is what you're assuming. I want something like a utility function that will retrieve some elements based on it being matched with DOM objects. Imagine me doing something like `var obj {}; $('a').bindTo(obj);` I want to be able to get the `a`s by doing something like `$.retreive(obj);` But I don't want it to alter `obj` in the process – qwertymk May 08 '12 at 17:17
2

You're looking for $.data. This method associates any JavaScript object or primitive with a DOM element. Under the hood, it's not adding the data as an expando to the DOM element or anything--instead, jQuery maintains its own object cache of DOM elements and data hashes. But that's under the hood; the point is, I think it's exactly what you're looking for.

$('#example').data('foo', { bar: 'quux' }); // returns the jquery object containing '#example', like most jQuery methods

Then, later:

console.log($('#example').data('foo')); // returns {bar: 'quux'}
zetlen
  • 3,609
  • 25
  • 22
2

I dont think this is easily achievable. Let me clarify:

To achieve what you want you would require a hashmap that allows objects in the position of keys. JavaScript does not (yet) support objects as keys in hashmaps though. So, for example, the following does not work:

var key = {value: 'key'};
var data {value: 'data'};
var map = {};

map[key] = data;

There are other solutions to achieve this in current javascript implementations, eg. a double lookup:

var key = {value: 'key'};
var data {value: 'data'};
var map = { keys: [], data: [], get: function (key) {
  var k = this.keys.indexOf(key);
  if (k >= 0) {
    return this.data[k];
  } else return undefined;
}, set: function (key, val) {
  var k = this.keys.indexOf(key);
  if (k < 0) {
    k = this.keys.push(k) - 1;
  }
  this.data[k] = val;
} };

map.set(key, data);
map.get(key).value;

This implementation however is of a terrible performance. There is a proposal for a so called WeakMap in JavaScript Harmony. Firefox I believe is currently the only browser implementing them, though. Since the feature required is not widely available and workarounds are of poor performance I would recommend trying to figure out a different way of achieving what you are trying to.

Daniel Baulig
  • 10,739
  • 6
  • 44
  • 43
1

As I understand, you're looking for some sugar way to run multiple named searches on the DOM and have results filtered in a namespace object.

If so, I guess the following jquery extension might be helpfull to you:

$.fn.seek = function (selectors) {
  var container = this,
    found = {};

  $.each(selectors, function (name) {
    if ($.isPlainObject(selectors[name])) {
      found[name] = $(container).seek(selectors[name]);
    }
    else if ($.type(selectors[name]) === 'string') {
      found[name] = $(container).find(selectors[name]);
    }
  });

  return found;
}

And here's the example of how the above extension might be applicable to your cases:

var res = $('body').seek({
  links: 'a',
  headers: 'h1,h2,h3,h4,h5,h6'
});

$(res.links).css({ color: 'green' });
$(res.headers).css({ color: 'red' });

I hope this helps you.

mcmlxxxiii
  • 913
  • 1
  • 9
  • 21
1

Not sure if this is what you are looking for. Perhaps you could write a custom selector based on the jquery selector, which handles objects with a selector-property the way you like. A selectable object would look like

var objSelector = {'selector' : '#select-me', 'a' : 'somestring', 'b' : 1243}; 

So you are free to use it like any other object, but you have to add the selector property. Than you add your custom selector:

$$ = (function($) {
    return function(el, tag) {
        if (typeof el === 'object' && el.selector !== undefined) {
            return $(el.selector);                  
        }
        return $(el);
    }
}($));

Now you can do things like

$$(objSelector).css({'border':'1px solid red'});

See an implementation on http://jsfiddle.net/JXcnJ/

daniel.auener
  • 1,276
  • 2
  • 12
  • 17
1

If I understood correctly then, I think you need to define a property and say enumerable as false. See below,

Note: Below is just an example to demonstrate and not exactly meant to do such stuff,

DEMO

$(function() {

    $.fn.bindTo = function(o) {
        var _that = this;

        Object.defineProperty(o, 'myFx', {
            value: function() { return $(_that); },
            writable: true,
            enumerable: false,
            configurable: true
        });
    }

    $.retrieve = function(obj) {
        return obj.myFx();
    }

    var obj = {
        prop: 'prop'
    };

    $('#test').bindTo(obj);

    $($.retrieve(obj)).html('Test');

   //below is for proof
    for (i in obj) {
        alert(obj[i]);
    }
});

Reference: http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/

Selvakumar Arumugam
  • 79,297
  • 15
  • 120
  • 134
1

Extend jQuery with three methods:

jQuery.bindObj(data)
jQuery.unbindObj(data)
$.retrieve(data)

Your code looks like:

$('a').bindObj({blorg: 'shmorg'});
console.log($.retrieve({blorg: 'shmorg'})); // logs live result of $('a');

Full source: http://jsfiddle.net/nUUSV/6/.

The trick to this solution is storing the selectors/identifiers based to the jQuery constructor in one array, and the objects bound to those selectors/identifiers in another array, then using $.inArray to get the index of the object upon retrieval and using that index to grab the bound jQuery collection.

benastan
  • 2,268
  • 15
  • 14