Why is jquery's .data() method needed - javascript

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.

Related

Custom Event Dynamic Action based on $(document) jQuery selector

I've been using dynamic actions based on custom events in one of my Oracle Apex pages. I am binding my event to the document and then using a 'DOM Object' selection type (of document) in order to specify the context for the event.
This works in Apex 4.2, however I have just come across this in relation to Apex 5:
https://docs.oracle.com/cd/E59726_01/doc.50/e39143/toc.htm#BAJDAGJG
5.10 Deprecation of Dynamic Actions Based on DOM Objects
Dynamic actions based on DOM Objects have been deprecated. Change your
dynamic actions to use a jQuery Selector or JavaScript Expression
instead of DOM Object.
My question is, how can I use a jQuery selector in order to detect events bound to the document? When I try using a jQuery selector of document, the dynamic action does not fire. I strongly suspect that this is because APEX wraps the selector in quotes when the dynamic action is parsed, rendering it useless for selectors on the document or window objects.
I am already aware that in the standard jQuery world I would just use $(document).
I already know that I can bind events to different DOM elements. I'm not interested in that. I am interested specifically in binding to document.
jQuery selectors return element nodes. Your event is bound to the document node, so there's no way to get at it with a jQuery selector. $(document) is not strictly speaking a selector. I believe $(":root").parent() returns the document object but that doesn't help you, since Oracle only lets you use selectors, not methods.
Oracle got back to me earlier with my Apex 5 workspace, so I've been having a play. The solution is in the documentation you quoted. You can't use a jQuery selector in your dynamic action's Selection Type, but you can simply use a Javascript Expression, with the value: document
I tested this by creating a button pointing to the URL:
javascript:apex.event.trigger(document,'testEvent');
I created a dynamic action responding to the Custom Event testEvent, Selection Type Javascript Expression, expression value document. It works fine, and the button now triggers an alert via a custom event handled at the document.
Short Example: How a dynamic action custom event placed ( oracle apex 18.1 ) to refresh interactive report section :

Execute JS function each time element with specified selector is added

I got to refactor big one-page application with complex UI.
There is following code in document.ready function
$('table.datatable').dataTable({ ... params ... });
$('div.tooltip').tooltip({ ... params ... });
$('ul.dropdownMenu').menu({ ... params ... });
As you can see in this code we search for different HTML controls and call to appropriate jQuery plugins that implement these controls' behavior.
However, since the page is very dynamic new datatables, tooltips and menus are added all the time and since JS functions were called at very beginning of application - those elements have no needed functionality unless I manually call appropriate plugin for them.
I'd like to eliminate the need to call jQuery plugin after each DOM change but don't know how to do it better.
One option is to fire an event each time I am adding anything to DOM and re-call this plugins in event's listener, however I don't like this solution because of need to remember fire the event each time.
I read about jQuery's on function which attach events also for not yet exists elements, but what event do I need? AFAIK there is no domchange event.
Any advises?
Your only option is to hook up your plug-ins on new items after they are added to the DOM. The best cross browser option would be to hook into each place that adds these types of elements to the DOM and simply deal with the new items there by calling some additional function on them. What you need to look for is where you can easily hook into your existing code after new elements have been added to the DOM and then fix up those new elements at that point. Without knowing your code and where the DOM is modified, we can't really advise the best way to do that.
Generically monitoring the DOM for new items with a single method is not possible in a cross browser fashion. Mutation observers are the latest standard way to do this, but it is not supported in many browsers yet. Mutation events came before mutation observers, but is now deprecated.
jQuery's .on() will not do what you want here - it can be used to handle dynamically added DOM elements, but not in the way you want. Your plug-ins could have been designed to handle their events with use .on(), but unless they were designed that way and you can take advantage of that, there isn't a simple way for you to use it to get your desired behavior without rewriting a portion of the plugin.

Javascript Objects and the DOM

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.

Is there a good way to attach JavaScript objects to HTML elements?

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.

How can I force the DOM to re-eval in Javascript/jQuery?

I am dynamically appending HTML to a webpage and I'm also using jQuery to manage stuff.
When I add HTML code, jQuery ignores its existence.
For example:
$("td.elementToClick").click(...
Will work great with jQuery. But if somewhere in the code I append:
$("tr#myRowToAppend").append("<td class="elementToClick>...</td>");
jQuery will ignore this new element if I click on it.
As jQuery associates the events after the page finishes loading, I need one of two solutions:
- Force the DOM to re eval the page without changing the current layout (I don't wish a refresh, so location.reload() is out of the question).
- Force jQuery to add this new element to it's internal event manager.
I don't wish to use onclick="blabla()", I really need to use jQuery.
How can I accomplish this?
What you are looking for is jQuery live. From docs description: "Binds a handler to an event (like click) for all current - and future - matched element. Can also bind custom events."
There is also a plugin liveQuery that supports a wider range of events if you want.
the live() method will alleviate most of your headaches.
I see this happening more often in IE and with cloned elements, to support IE you have to be much more careful with DOM manipulation.
I also see alot of questions on SO with people having issues of copying/moving dom elements to new parts of the dom without cloning it first, which doesn't workout so well in IE.
So you can use live or when you have to handle events from dynamically inserted DOM elements, make sure you clone them with clone(true) to specify you want the events copied:
$("body").append('<div id="one"></div>");
$("#one").mouseover(function(){});
$("body").append( $("#one").clone(true).attr('id','two') );

Categories