updateevents for joint.uml.shapes.State element in jointjs - javascript

Merry Christmas, everyone !
I have to updatethe events for joint.shapes.uml.State element, which is introduced by JointJS library, after some events are triggered. I use the set('events', events) function. I print the element to the console, and find the element's events has already been updated. But the events shown on the graph haven't changed. The sample code can be accessed at:
http://jsfiddle.net/GJH_ICT/6pmbQ/5/.
You can drag the end of a link to trigger events. This is the update function:
function resetEvents(id, old, newer) {
var events = graph.getCell(id).get('events');
for(var i = 0; i < events.length; ++i) {
events[i] = events[i].replace(old, newer);
}
graph.getCell(id).set('events', events);
console.log(graph.getCell(id).get('events'));
}
Besides, I have noticed the updateEvents() function in joint.shapes.uml.State element, and tried that function, but it does not work fine. What does this function do?
Thanks!

This is because you're changing the events array in place. When you call .set() later on, the internal mechanism of set() thinks it is exactly the same object - the reference is the same - in other words, the === operator between the object passed and the one that has been previously stored returns true.

Related

For loop doesent work while trying to add elements

Ok so i have a sidenav menu and it has 3 levels and the second layer has openers but the third layer doesent so i have to add openers like this ▼ via javascript but there are too many of them so i had to use a for loop too give each of the second layers an opener,i already created an object named opener2 like this
var opener2=document.createElement("span");
opener2.classList.add("opener");
aand then here is the for loop that doesent work
for(var q=0; q < $('.menu-block').length; q++){
if($('.menu-block')[q].childElementCount >=2){$('.menu-block')[q].before(opener2)};};
so let me explain menu-block is the part that contains both the opener and an item of the second layer,like there are 5 of them and each are different from each other,if there are 2 elements inside the second layer items menu block,that means this for loop shouldnt add an opener.Soo what should i do?
Edit:The problem is solved thx for your help guys
for(var q=0; q < $('.menu-block').length; q++) {
if($('.menu-block')[q].childElementCount >=2){
let opener2=document.createElement("span");
opener2.classList.add("opener");
$('.menu-block')[q].before(opener2)};
};
Declare the object declaration inside the for loop or else the same element will get appended again and again
The problem is that you have created only one span element, so this will not magically create new ones. If you have a second call to .before, it will actually move the span from its previous insertion point to the current.
It is not so efficient that you repeatedly reselect the elements with the ".menu-block" selector. Just do that once, and use .each to iterate over that collection.
Also, you are mixing jQuery code with jQuery-less code, which is a pity. If you use jQuery, you should not have to use things like document.createElement.
So here is how you could do it:
$('.menu-block').each(function () {
if (this.childElementCount >= 2) {
$("<span>").addClass("opener").insertBefore(this);
}
});

Do Javascript engines do this kind of optimisation?

NB I've done a bit of reading about JS engine optimisation, but most of it is too technical for me to understand enough to apply to this question. I'm also aware that not all engines are the same. I'd be interested in particular in handling by V8 and Rhino, I suppose.
If I create a table, and then rows, and then cells... And then I want to put identical key event listeners on all those cells.
Not only does the creation of these listeners for each cell take a certain amount of time, which could be significant with a biggish table, but in addition I'm supposing that each listener function is stored on its own, even though every listener function is actually identical.
The other key event listener approach which I can use is to put a key event listener on the TABLE, and to work out during the run, on each keydown event, which cell fired this event. I can do this by going
let elementOfInterest = document.activeElement;
"Get the currently focused element in the document" from here.
From my experiments, if you type inside a table cell, this TD does indeed have the focus and is indeed returned by the above call.
This way, I only have to create one listener, which will I assume be quicker and take less memory. The only (very) slight downside is that time then has to be spent getting this "active element" by means of the above call. And, just possibly, the risk that something will grab focus in an unexpected way - obviously if you want to listen to changes of text in a cell, the least error-prone technique must be to use a listener attached to that cell.
But I'm just wondering: maybe Javascript is cleverer than this: maybe if you create 100 separate cell listeners something somewhere identifies them as "all the same" and just makes one function in memory. This is the kind of optimisation you might typically expect from a Java compiler, for example.
Does any such optimisation ever occur? How clever is Javascript with a case like this? Or is it just "script and that's it": what you see is what you get?
The semantics of the language itself don't allow for two function expressions to be "merged" into one even if they were functionally equivalent:
> a = function(){return 'foo'};
ƒ (){return 'foo'}
> b = function(){return 'foo'};
ƒ (){return 'foo'}
> a === b
false
In addition, things get extra hairy when you start considering the closure of the function (e.g. the outer names it uses).
So no, that doesn't happen out of the box.
However, for your use case, there are two optimizations:
As you've found out, you can employ event bubbling and add the event listener on an ancestor element and use event.target (preferably instead of document.activeElement) to figure out where it was originally targeted (and event.currentTarget would be the node the handler is on)
If you can't use a common ancestor (tip: you almost always can; document is a valid target), you could define the function once (assuming it doesn't need to close over any dynamically changing variables) and again use event.target, e.g. event.target.dataset to figure out the data you're handling.
Below, a snippet demonstrating the two.
function createButton(parent, datum) {
const btn = document.createElement("button");
btn.dataset.datum = datum;
btn.innerHTML = datum;
parent.appendChild(btn);
return btn;
}
function eventHandler(event) {
if(event.target.tagName !== "BUTTON") return;
const msg = `real target: ${event.target} (datum="${event.target.dataset.datum}")\ncurrent target: ${event.currentTarget}`;
alert(msg);
}
const p2 = document.getElementById("parent2");
// bubbling listener
const p1 = document.getElementById("parent1");
p1.addEventListener("click", eventHandler, false);
for(var i = 0; i < 10; i++) {
createButton(p1, "p1-" + i);
}
// same function on multiple elements
for(var i = 0; i < 10; i++) {
createButton(p2, "p2-" + i).addEventListener("click", eventHandler, false);
}
<div id="parent1"></div>
<div id="parent2"></div>

Raphael.js - registering multiple events to element

my problem is that I need handle multiple events for rectangle. That sound simple,
for example this works
node.click(function(e){
click(); // this is function defined in same scope, it works ok
});
node.mouseout(function(e){
mouseout();
});
But, I want to automatize this, so it should looks like this:
var events = new Array("click", "mouseout");
for(var i in events){
node[events[i]](function(e){
events[i](); /*THIS is problem, no matter if it is click or mouseout
this always fires function with same name as last item
in events array (in this case mouseout)
*/
}
}
Do you have any idea why a how I should solve it?
Your handlers created in a loop are sharing a variable. By the time they are called, the variable is the last value in the loop.
You have to use a technique I call "freezing your closures" so that each handler gets a separate copy of the shared variable. In your case, the shared variable that changes is i
Your other problem is that you want to call your functions "click/mouseout" from a string, so you have to get a handle to the function, right now your code is attempting to call "hello"() which does not work
Your last problems (but not a bug yet) are that you shouldn't use the Array constructor and you shouldn't use a for in loop to iterate over arrays.
function createHandler(eventName) {
return function(e) {
window[eventName]();
}
}
var events = ["click", "mouseout"];
for(var i=0; i < events.length; i++){
node[events[i]](createHandler(events[i]));
}
The above example is easier to comprehend but you could use self invoking anonymous functions to do the same thing
var events = ["click", "mouseout"];
for(var i=0; i < events.length; i++){
node[events[i]]((function(eventName){
return function(e) {
window[eventName]();
};
})(events[i]));
}

Avoiding duplicate objects in JavaScript dojo

I'm creating an object onfocus of an DOM Element to handle various subsequent events. However, after the element looses focus, I need to destroy the object somehow, so that future focus on the element creates a fresh object with no reference to the original.
Right now, this issue I'm having is that subsequent clicks create another object and all the functions are called twice. Click again, three times. Etc. The code below shows an example of the click event and object creation.
Any help would be appreciated.
var videoTopicsHandler = DojoOn(videoTopicsInput, 'focus', function(e){
dropKeyPress(this, 'video-topics');
});
var dropKeyPress = function(input, ulId, scroll) {
var handler;
obj = new dropDownObj(ulId, scroll);
obj.attachEvents(obj, handler, input);
};
Try this:
var obj = null;
var dropKeyPress = function(input, ulId, scroll) {
var handler;
if(obj !== null) obj.destroyRecursively();
obj = new dropDownObj(ulId, scroll);
obj.attachEvents(obj, handler, input);
};
Instead of deleting, you may want to cache those objects you've created and pick them up from cache if it is already there when element regains focus. Pros: faster, no need to regenerate objects on each re-focus and destroy them; cons: unused objects will take memory, but this could be irrelevant if you either go to next page fast enough, or there's small number of those object and re-focusing happens often enough so you'll need them anyway.

List all live events in jQuery

How could I find in jQuery what events are bound with live for a particular element?
Say I have a function, randomFunction, that returns a random function from an array of functions. How can I find which function has been bound to a certain element?
var arrayOfFunctions = []; //a whole bunch of functions
function randomFunction(array){}; //returns one of those functions
$('#certain_element').live('click', randomFunction(arrayOfFunctions));
What is the index of the array that corresponds to the function that was bound by live for $('#certain_element')?
Alright, figured it out.
For a click event, for $('#certain_element'), logging each binding's index to the console:
var relevantHandlers = $.map($(document).data('events').live, function(value){
if(value.origType == 'click' && value.selector == '#certain_element'){
return value.handler;
}
}; //all handlers for #certain_element bound to click by live.
$.each(relevantHandlers, function(){
console.log("the index is: " + $.inArray(this, arrayOfFunctions));
});
Take a look at this plugin. When I last used this, there was a need to slightly modify it for the then latest version of jQuery, but it should give you a direction.
There's a nifty bookmarklet called Visual Event that shows the code that will be called.
But since you're truly calling a random function, maybe doing something as simple as including an alert("function name") or colsone.log("function"), if you're just testing.

Categories

Resources