I'm making a little script right now and one of the inputs for an arg of the main function is an element. In order to make sure that raw JS works for this stuff I'm doing theelement.value to get the variable's value. Unfortunately, that doesn't work if thelement is a jquery function, like $('#foo'). Some other reasons I need this kind of compatibility is that I need access to theelement.style, once again something jquery doesn't support. I know there are jquery workarounds for these (.css(), .val() etc.) but if I use those, then regular js inputs (document.getElementbyId('foo')) won't work. Is there any way to use one "property" and make it universal against all inputs of elements (including stuff from libraries like prototype and Dojo)?
No it is not possible to make everything universal.
If you want to work on DOM elements, the input needs to be DOM elements and the framworks all have ways of getting the DOM elements out. You can not magically make a script that works with any library out there, that is why generic plugins normally have library specific extensions to add support.
It sounds to me like you have a function that takes an argument and you have issues when you pass different types as that argument:
function doSomething(element) {
// do something with element
}
You've mentioned that you want element to sometimes be a jQuery object and sometimes just a DOM object and then you also mentioned other libraries too (prototype, Dojo).
First off, your problem will go away entirely if you just call your function with a consistent type. The simplest type here that is available in all libraries is a DOM element. So, if you just enforce that element is passed as a DOM object reference and not a jQuery object or any other library object, then your problem will go away entirely. If the caller is using some other library and then wants to call your function, they can always get the DOM object from that library and pass the actual DOM object. For example, in jQuery, you can get a DOM object like this `$("#foo").get(0)
If you want to make it more universal and accept multiple types, then you have to write code to detect each type that might be passed and get the DOM object out of each type so you can then process them all identically. For example, if you wanted to detect either a jQuery object of a DOM object, you could do so like this:
function doSomething(element) {
// normalize element to be just a DOM object
// if it's a jQuery object, then reach in to get the first DOM object
if (element.jquery) {
element = element.get(0);
}
// do something with element here which is a DOM object
}
If you wanted to detect objects from other libraries too, then you'd have to write similar detection code for each other library and get the actual DOM object out of that other library's object.
Related
I often need to register a single-purpose event listener to multiple HTML elements (e.g. change, click), in a loop.
I need a way to differentiate between the target elements when the event fires. Ideally the listener should have a variable that changes depending on which element fires the event.
A straight-forward solution would be to set the id attribute in the loop (jsfiddle), and refer to this.id within the handler. Or if additional fields are required, the data-* attributes, and instead accessing this.dataset.id.
However these end up adding to the actual markup of the page, and can only store strings. If you have a lot of data to assign, of specific types other than string, it can be impractical, and require unnecessary type checking, or integer parsing.
I got around this by defining a new property called param on each HTML element, and I could access it in the handler like so: this.param.address. But that turns out to be a bad idea - a 'feature' not to be relied on due to browser consistencies and a lack it being in any standard.
Another way is supplying arguments to the listener through a closure (jsfiddle). The downside of this method is that it returns a unique listener for each element, which might affect performance versus just a single handler.
Yet another method I had thought of was storing the actual element references with data in an array, and performing a lookup of this from the event handler (JSFiddle).
So my question is: Is there a better (and correct/future-proof) way to pass/assign data to element event handlers, without having to store it as an attribute string?
P.S. I don't want to have to rely on jQuery for this either.
With jQuery it's possible to use $(el).bind("click", {foo:i}, buttonClicked) and $.data(el, "param", {foo:i}) to accomplish the task. However it seems to rely on being able to set a custom property to the element's object, much what I proposed for using param as a custom property for my purposes. Does that mean it's fine as long as the property name is unique enough?...
Say I have a container element/div on a page. There are other elements outside of it, comprising page layout/navingation, and there is a fair amount of content inside of it.
In my javascript, I need to find/manipulate a few elements inside of that container. If all of these elements have id properties set, speed wise, is it better to use document.getElementById() (considering that once found, the element will need to be turned into a jQuery object for manipulation) to find them, or to select them using jQuery like so: $("#id"), or to select the container first, store it in a variable, and then select the elements to be manipulated using jQuery like so: container.find("#id")?
Which approach is faster? What does the speed of searches like these depend on?
As jQuery() with an id selector uses document.getElementById inside, using the native method directly is obviously faster. Of course the overhead is neglibible (you never need to select hundreds of elements by id), and they do different things - most likely you want to get a jQuery wrapper object containing the DOM node.
container.find(idselector) is an interesting case, though. As ids are unique all over the document, you should not need such an expression at all. Because of that, id selections are usually boosted by a O(1) lookup table inside the browser, and very fast even for full documents. I'm not sure what happens when you use it inside a container, but it might fall back to a rather slow DOM tree traversal.
In the following getElementsByTagName("p")[0] and getElementById("demo") access the same element.
Both of the following work, so I can't figure out why the jquery data function is even needed. Is the second not portable to all browsers.
$(document.getElementsByTagName("p")[0]).data("funcZ", function() {console.log("ZZZZZ")})
$(document.getElementById("demo")).data("funcZ")()
document.getElementsByTagName("p")[0].funcX = function() {console.log("XXXXX")}
document.getElementById("demo").funcX()
According the the jQuery website:
The jQuery.data() method allows us to attach data of any type to DOM elements in a way that is safe from circular references and therefore free from memory leaks. jQuery ensures that the data is removed when DOM elements are removed via jQuery methods, and when the user leaves the page.
It's possible that by attaching random fields to a DOM element, when the DOM element disappears, the fields remain in memory. It looks like jQuery handles that for you.
The $.data() method is perfect for hiding data as opposed to attaching it to a data-attribute. It's easily accessed by key/value, great for storing state information when creating plugins, or really anything.
So we all know you can create JavaScript objects quite simply as:
var myObject = new Object() ;
myObject.attribute = someValue ;
myObject.doSomething = someFunction ;
We can then use that object within JavaScript to manipulate the DOM using the standard techniques one would except eg:
document.getEmelemntByID("some ID").someAttribute = myObject.attribute;
Now this is simply using the facilities of JavaScript to query the DOM to find some element and then set an attribute, all well and good. You can do the same thing with jQuery and other tools, all of which are based on JavaScript.
One can call a JavaScript function with any of the various methods the DOM provides such as onClick, onChange, etc. etc.
So my question is how does one actually have a JavaScript object IN the dom, with say a draw method a click method, etc. etc. This would allow DOM to execute the draw method to render something on the DOM canvas and then call a method particular to that object on say click or double click?
The DOM tree is made up of Javascript objects, specifically different kinds of element objects. You can't put other objects in the DOM tree.
The DOM elements uses events for customisable actions, not methods. The click event for example is activated when someone clicks on the element. You hook up a method as event handler for an event, and that method will be called when the event is activated.
You can't make your own types of objects in the DOM and create new browser level events with javascript. You must create one of the types of objects that the browser DOM already supports and use the events that those objects support. It is possible to use native code to create browser plug-ins (like Adobe Flash) that will do some of what you're asking, but I don't think that's what you wanted as those aren't implemented in javascript and require downloads.
You can however use a generic object like a <div> and respond to click events on it in any way you want. Since a div has no default behavior for clicks and drawing, it's just a placeholder in the DOM. You can then embed your own objects inside of it or set properties on it to create whatever visual look you want. And, you can handle the various events on it (clicks, keys, etc...) to give it the desired behavior. So, you can think of a generic object like a <div> as scaffolding which you can build your own object look and behavior on.
There are even ways to put a javascript wrapper around the DOM object so most of your interaction with that object appears to be with your javascript object instead of with the actual DOM object (that's what both jQuery and YUI do). But, underneath, it's still a real DOM object with your own javascript wrapper around it.
I want to associate a JavaScript object with an HTML element. Is there a simple way to do this?
I noticed HTML DOM defines a setAttribute method and it looks like this is defined for arbitrary attribute name. However this can only set string values. (You can of course use this to store keys into a dictionary.)
Specifics (though I'm mostly interested in the general question):
Specifically, I have HTML elements representing nodes in a tree and I'm trying to enable drag-and-drop, but the jQuery drop event will only give me the elements being dragged and dropped.
The normal pattern for getting information to event handlers seems to be to create the HTML elements at the same time as you are creating JavaScript objects and then to define event handlers by closing over these JavaScript objects - however this doesn't work too well in this case (I could have a global object that gets populated when a drag begins... but this feels slightly nasty).
JavaScript objects can have arbitrary properties assigned to them, there's nothing special you have to do to allow it. This includes DOM elements; although this behaviour is not part of the DOM standard, it has been the case going back to the very first versions of JavaScript and is totally reliable.
var div= document.getElementById('nav');
div.potato= ['lemons', 3];
If you're already using jQuery, you can use its data() method for this. This allows you to assign complex objects to the element if you want or you can leverage that method to hold a reference to an object (or some other data) at the very least.
It's worth noting that, under the hood, jQuery implements data() in precisely the way that bobince described in his answer, so you always use that directly, whether or not you're using jQuery.