Event bubbling/capturing/delegation in ember - events

It is hard to expliain the actual code as i dont exactly know where the problem is, but it will be great if anyone can help in explaining how events are delegatd/bubbled in Ember.
I have a case where mouse move on document is not triggered inside one view. This happens when the view gets inserted dynamically using outlets.
MOUSE MOVE BINDING CODE:
MOVE_EVENT = touch ? 'touchmove' : 'mousemove',
$(document).on(MOVE_EVENT, function (e) {
console.log('move event move=true');
if (move) {
e.preventDefault();
stop = getY(e);
//console.log('move event move=true');
var val = pos + (start - stop) / h;
val = val > (max + 1) ? (max + 1) : val;
val = val < (min - 1) ? (min - 1) : val;
inst.scroll(target, val);
}
});
If I load the page with a router URL that shows the view in the outlet by default, the mouse move inside the view triggers mousemove on body. And when I gets into the view by changing states it doesnt happen: here is a log of a statechage that leads to the behaviour.
mouse move in repeat view
move event move=true
mouse move in repeat view
move event move=true
STATEMANAGER: Sending event 'chooseSkipTime' to state root.ote.
STATEMANAGER: Entering null
STATEMANAGER: Entering root
STATEMANAGER: Entering root.ote
STATEMANAGER: Entering root.ote.repeat
STATEMANAGER: Entering root.ote.repeat.skiptimes
STATEMANAGER: Entering root.ote.repeat.skiptimes.index
skiptimes view Inserted
skiptimes is inserted into an oulet on RepeatView. After such a state change the mouse move inside repeat view is not getting triggered, or it is not triggering the mousemove on document. At this same time as I move mouse outside the area of the parentview(RepatView) then mouse move shows log.
In a nutshell mousemove inside a view is not triggered. I have put:
mouseMove : function(e){
console.log('Mouse Move in Blah Blah View');
}
in every view inside RepeatView including RepeatView, none triggers. But mouse move on Parent of RepeatView and upwards is triggering as logs are shown.
It is hard to expliain the actual code as i dont exactly know where the problem is, but it will be great if anyone can help in explaining how events are delegatd/bubbled in Ember.
UPDATE
After further investigation it looks like the following stopPropogation in ember-latest is causing this, not yet able to understand why its going to else.
setupHandler: function(rootElement, event, eventName) {
var self = this;
rootElement.delegate('.ember-view', event + '.ember', function(evt, triggeringManager) {
var view = Ember.View.views[this.id],
result = true, manager = null;
manager = self._findNearestEventManager(view,eventName);
if (manager && manager !== triggeringManager) {
result = self._dispatchEvent(manager, evt, eventName, view);
} else if (view) {
result = self._bubbleEvent(view,evt,eventName);
} else {
//evt.stopPropagation();
}
return result;
});

Related

Removing event after one scroll

I have a page with a full screen revolution slider on it. I need for the slides to advance on the scroll wheel. The code I have below uses the scroll event (not mousewheel) to advance through the slides but has one inherent problem. If you really spin the mousewheel, it will scroll through the entire deck all at once and you really can't control where you end up. I was hoping to remove the listener once one use of the scroll wheel has been detected and then add it again right after the slide changes ... something like that ... I'm only guessing ... or maybe it could somehow constrain the scroll event to one click?
What I have now in my document ready code block:
$(function(){
var _top = $(window).scrollTop();
var _direction;
$(window).scroll(function(){
var _cur_top = $(window).scrollTop();
if(_top < _cur_top)
{
revapi17.revnext();;
}
else
{
revapi17.revprev();;
}
_top = _cur_top;
console.log(_direction);
});
Reference for revolution slider API: http://clapat.ro/themes/creative/doc/rev-documentation.html#!/publicapi
dev URL: http://devonline.wpengine.com/story-2/
UPDATE: Thanks everyone. I found a solution:
// bounce if slider isn't present on the page
if(typeof revapi17 === 'undefined') return;
// add wheel listener when slide changes
revapi17.on('revolution.slide.onchange', onChange);
function onChange() {
// remove wheel listener before adding it to ensure event only fires once
revapi17.off('DOMMouseScroll mousewheel').on('DOMMouseScroll mousewheel', onWheel);
}
function onWheel(event) {
// remove wheel listener as soon as wheel movement is detected
revapi17.off('DOMMouseScroll mousewheel');
event = event.originalEvent;
if(event.wheelDelta > 0 || event.detail < 0) {
revapi17.revprev();
}
else {
revapi17.revnext();
}
}

Kendo UI Grid Editor Complete

I have a Kendo Grid, where I have defined an Editor like this:
#(Html.Kendo().Grid(Model.Data)
.Name("GridINT")
.Editable(editable => editable
.Mode(GridEditMode.PopUp)
.TemplateName("MyTemplateName")
.Window(w => w.Width(500))
.Window(w => w.Title("My Template")))
Before I engage the editor, I bind a mouseup handler to the rows, and I tweak the style of the command button. When the editor closes, whether by Submit, Cancel, or 'X', my handler and style tweaks are gone for the affected row. I need to restore them, but I haven't found the valid event. I have bound the cancel click event like this:
$('.k-grid-cancel').bind('click', function ( e ) {
colorCommandCells();
});
but if I restore my handler/style to a grid row here, the editor's closing process undoes what I have done.
Bottom line: how can I know that the editor is finished updating the grid (which it does, as I have described, even if the editor is cancelled) and which row was the one that the editor messed with?
This is the code that colors the command cells:
function colorCommandCells() {
// This block colors the command cell according to ISNEW. It must run every time the DataBound event occurs.
var grid = $("#GridINT").data("kendoGrid");
var gridData = grid.dataSource.view();
for (var i = 0; i < gridData.length; i++) {
var currentUid = gridData[i].uid;
var currentRow = grid.table.find("tr[data-uid='" + currentUid + "']");
var editButton = $(currentRow).find(".k-grid-edit");
var aColor = gridData[i].ISNEW == 1 ? "#FFCCFF" : "transparent";
var aText = gridData[i].ISNEW == 1 ? "Add" : "Edit";
var parent = $(editButton).closest("td");
$(parent[0]).css('background-color', function () { return aColor; });
editButton[0].innerHTML = "<span class=\"k-icon k-edit\"></span>" + aText;
}
}
Basically the Grid is rebound each time after such operations, and it is good to either use delegate events attached to the tbody of the Grid or bind the events each time when the dataBound event of the Grid occurs.
There are two parts to this answer: First, when building the DataSource for the Grid, assign a function to the Sync Event.
.Events(e => e.Sync("syncGrid"))
Also, when building the Grid, assign a function to the Cancel event.
.Events(e => e.DataBound("gridIsDataBound").Cancel("cancelEditor").Edit("gridEdit"))
You have to have both, because the Sync event will fire if the popup editor is closed via "Submit" and the Cancel event will fire if the popup editor is closed via "Cancel" or "X". Both functions should call something like this, where colorACommandCell is where I restore my style values:
function closeEditor() {
var timer;
clearTimeout(timer);
timer = setTimeout(colorACommandCell, 100);
}
There is still some activity related to the Grid that occurs after the editor closes (this is what clobbers my style tweaks). I found that, if I queued up my "fixes" to wait 0.1 seconds, then they would not get overwritten. Ideally, however, I'd like to have a more certain event that fires when the Editor is REALLY finished. I don't expect to be able to trust this timer on every machine that runs my code.

Disable click event after touch and hold

I am developing an app iOS app in appcelerator and I got a table with user.
When I click on the user it opens the profile but I also want the user to be able to copy the name just by tap and hold for 2 seconds.
These two event works fine separately but right now after tap and hold the click event fires to. How can I prevent the click event from firing after tap hold?
// Set the timeout
var holdTime = 500, timeout;
// Create the table touch start event listener
table.addEventListener('touchstart', function(e) {
// Set the selected user id
var itemValue = e.row.value_full;
// Define the function
timeout = setTimeout(function(e) {
// Create the fade out animation
var fadeOut = Titanium.UI.createAnimation({
curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT,
opacity: 0,
duration: 1000
});
// Create the loading screen
var copied = UI_messages.showFlash({label : 'Copied!'});
// Add the loading screen
win.add(copied);
// Save value to clipboard
Titanium.UI.Clipboard.setText(itemValue);
// Fade the message out
copied.animate(fadeOut);
}, holdTime);
});
// Create the event listener for touch move
table.addEventListener('touchmove', function() {
// Clear the timeout
clearTimeout(timeout);
});
// Create the event listener for touch move
table.addEventListener('touchend', function(e) {
// Clear the timeout
clearTimeout(timeout);
});
I've run into this problem before as well. The solution I used isn't very pretty, but it's the only effective way that I've found to suppress a touch event after a touch-and-hold.
The only working solution that I could find was to create a bool variable in a global namespace. In your setTimeout function, change the value of the bool to true to indicate that a touch-and-hold has occurred.
In the onClick event event for the row, check the global variable first to see if you've already created a touch-and-hold event - if you have, just return from the onClick event. This will effectively disable your click event when a touch-and-hold occurs.
Remember to set the global variable to false after the touch-and-hold function ends.

jQuery $.get being called multiple times...why?

I am building this slideshow, hereby a temp URL:
http://ferdy.dnsalias.com/apps/jungledragon/html/tag/96/homepage/slideshow/mostcomments
There are multiple ways to navigate, clicking the big image goes to the next image, clicking the arrows go to the next or previous image, and you can use your keyboard arrows as well. All of these events call a method loadImage (in slideshow.js).
The image loading is fine, however at the end of that routine I'm making a remote Ajax call using $.get. The purpose of this call is to count the view of that image. Here is the pseudo, snipped:
function loadImage(id,url) {
// general image loading routine
// enable loader indicator
$("#loading").show();
var imagePreloader = new Image();
imagePreloader.src = url;
loading = true;
$(imagePreloader).imagesLoaded(function() {
// load completed, hide the loading indicator
$("#loading").hide();
// set the image src, this effectively shows the image
var img = $("#bigimage img");
img.attr({ src: url, id: id });
imageStartTime = new Date().getTime();
// reset the image dimensions based upon its orientation
var wide = imagePreloader.width >= imagePreloader.height;
if (wide) {
img.addClass('wide');
img.removeClass('high');
img.removeAttr('height');
} else {
img.addClass('high');
img.removeClass('wide');
img.removeAttr('width');
}
// update thumb status
$(".photos li.active").removeClass('active');
$("#li-" + id).addClass('active');
// get the title and other attributes from the active thumb and set it on the big image
var imgTitle = $("#li-" + id + " a").attr('title');
var userID = $("#li-" + id + " a").attr('data-user_id');
var userName = $("#li-" + id + " a").attr('data-user_name');
$(".caption").fadeOut(400,function(){
$(".caption h1").html('' + imgTitle + '');
$(".caption small").html('Uploaded by ' + userName + '');
$(".caption").fadeIn();
});
// update counter
$(".counter").fadeOut(400,function() { $(".counter").text(parseInt($('.photos li.active .photo').attr('rel'))+1).fadeIn(); });
// call image view recording function
$.get(basepath + "image/" + id + "/record/human");
// loading routine completed
loading = false;
}
There is a lot of stuff in there that is not relevant. At the end you can see I am doing the $.get call. The problem is that it is triggered in very strange ways. The first time I navigate to a tumb, it is called once. The next time it is triggered twice. After that, it is triggered 2 or 3 times per navigation action, usually 3.
I figured it must be that my events return multiple elements and therefore call the loadimage routine multiple times. So I placed log statements in both the events and the loadimage routine. It turns out loadimage is called correctly, only once per click.
This means that it seems that the $.get is doing this within the context of a single call. I'm stunned.
Your problem may be:.imagesLoaded is a jQuery plug in that runs through all images on the page. If you want to attach a load event to the imagePreloader only, use
$(imagePreloader).load(function() {
...
}
Otherwise, please provide the code where you call the loadImage() function.
Update:
when clicking on a thumb That is the problem. $(".photos li a").live('click',... should only be called once on page load. Adding a click handler every time a thumb is clicked will not remove the previous handlers.
Another option is to change the code to $(".photos li a").unbind('click').live('click', ... which will remove the previously registered click handlers.

Livequery fires click no matter where the user clicks in the document

I have replaced the traditional select/option form elements with a nifty little popup window when a triggering image is clicked. The page is for accounting purposes and so multiple line items are to be expected. I've written the javascript that will dynamically generate new line item select/option elements. When the page loads, the initial set of choices loads and the user can click on them, get a pop up with some choices, choose one and then the box closes. The move to the next choice and so on and so forth. I've added livequery to my code for those dynamic elements. However... the livequery("click"...) seems to fire no matter where the user clicks on the page. Very frustrating.
I've read on here how great "live()" is in jQuery 1.3, but I am not able to upgrade fully to jquery 1.3 because a custom JS file depends on 1.2, so using live() is out of the question, however I have invoked the livequery() plugin and I really need to understand if I'm using it correctly.
I will post partial code. There's just way too much to post all of it.
Basically, I'm searching for divs starting with "bubble" and then a number afterwards. Then run the event on each them. Only bubble1 is static, 2 and up are dynamic. Am I missing the whole usage of livequery?
>$jb('div[id^="bubble"]').each(function () {
> var divid = $jb('div[id^="bubble"]').filter(":first").attr("id");
>var pref = "bubble";
>var i = divid.substring((pref.length));
>var trigger = $jb('#trigger' + i, this);
>var popup = $jb('#pop'+ i, this).css('opacity', 0);
>var selectedoption = $jb('selectedOption' + i, this);
>var selectedtext = $jb('selectedOptionText' + i, this);
>$jb([trigger.get(0), popup.get(0)]).livequery("click",
> function () {
>//alert(i);
// code removed for brevity (just the contents of the popups)
>});
Live works by using event delegation. A click event is attached to the body, and anytime something is clicked the selector is tested against the target. If it passes the selector test it calls the function (thus simulating a click event).
You probably want something like this:
$('div[id^="bubble"]').livequery("click", function() {
var divId = $(this).attr("id");
var i = divId.substring("bubble".length);
var trigger = $("#trigger" + i, this);
var popup = $("#pop" + i, this).css("opacity", 0);
// alert(i);
}

Resources