Regaining focus in iFrame with jQuery context menu - javascript

I've created a fiddle to reproduce the problem.
https://jsfiddle.net/rvwp47Lz/23/
callback: function (key, option) {
console.log("You clicked the test button", this);
// Need the iframe contents to regain focus so the mouse events get caught
setTimeout(function () {
$iframe[0].contentWindow.focus();
}, 100);
}
Basically, what I want to happen is the mouse move events to be caught after closing the context menu.
I can call focus on the iFrame's body or document but it doesn't seem to have any effect.
After you right click one of the items within the iframe and select an item, the mousemove event on the iframes body is no longer called (you can also notice that the hover CSS effect on the items are no longer working).
Ideas?

After some debugging and playing around with jQuery.contextMenu's code it seems the issue actually comes from the itemClick function. I added comments to the code and will add an issue to their github for a possible fix (unless there's some reason they're disabling default here)
// contextMenu item click
itemClick: function (e) {
var $this = $(this),
data = $this.data(),
opt = data.contextMenu,
root = data.contextMenuRoot,
key = data.contextMenuKey,
callback;
// abort if the key is unknown or disabled or is a menu
if (!opt.items[key] || $this.is('.' + root.classNames.disabled + ', .context-menu-submenu, .context-menu-separator, .' + root.classNames.notSelectable)) {
return;
}
// This line is causing the issue since it's preventing the default actions which puts
// mouse events back into place. Chrome must disable mouse move events when the contextmenu event
// gets triggered to improve performance.
//e.preventDefault();
e.stopImmediatePropagation();

Related

Google Maps API v3: allow default context menu on custom OverlayView

I've got a map with custom overlays (based on https://developers-dot-devsite-v2-prod.appspot.com/maps/documentation/javascript/examples/overlay-popup).
The custom overlay content includes a link/anchor tag and I would like to allow the user to right click the link and select "Open in new tab", however right clicks are cancelled by the map and I am unable to figure out how to prevent that behaviour.
If you compare the sample of the custom overlay linked above with the default Info Window https://developers-dot-devsite-v2-prod.appspot.com/maps/documentation/javascript/examples/infowindow-simple you can notice that the custom overlay does NOT show the context menu when right clicking on the "Hello World" text, whereas the Info Window DOES show the context menu. In the dev tools I noticed an event handler on the Info Window that somehow allows the context menu (removing that handler stops the context menu coming up), however as it's in the minified Google maps code I'm not able to make sense of it.
I have tried the following:
google.maps.event.addListener(map, 'rightclick', function (e) {
var event = e.ya;
var element = event.target;
if (element.nodeName === "A") {
event.stopImmediatePropagation();
event.stopPropagation();
return true;
}
});
Code is executed, but there's still no context menu. Instead it breaks something on the map as the map then moves with the mouse as if I still had the mouse down (looks like I prevented the mouseup handler).
I also tried to set preventMapHitsFrom on the custom overlay (https://developers.google.com/maps/documentation/javascript/reference/overlay-view#OverlayView.preventMapHitsAndGesturesFrom), which made the above not fire any more, but still no context menu.
I was also able to attach an event handler myself (excuse the jQuery):
$(document).on("contextmenu", ".map-popup__link", function (e) {
e.stopImmediatePropagation();
return true;
});
But again not sure how to prevent the event being cancelled. I also tried to trigger a new event on the same element, but that just creates a loop (obviously) without solving the problem.
Based on https://stackoverflow.com/a/7414594/1397352
I have modified the Popup.prototype.onAdd function to
Popup.prototype.onAdd = function () {
this.getPanes().floatPane.appendChild(this.containerDiv);
this.getPanes().overlayMouseTarget.appendChild(this.containerDiv);
// set this as locally scoped var so event does not get confused
var me = this;
// Add a listener - we'll accept clicks anywhere on this div, but you may want
// to validate the click i.e. verify it occurred in some portion of your overlay.
google.maps.event.addDomListener(this.containerDiv, 'contextmenu', function () {
google.maps.event.trigger(me, 'contextmenu');
});
};
Breakpoint in event handler gets hit, but again no context menu shows.
Has anyone got this to work with custom overlays?
Thanks to MrUpsidown's comment I was able to find a solution in the (archived) infobox library: https://github.com/googlemaps/v3-utility-library/blob/master/archive/infobox/src/infobox.js#L231
It looks like I was close in my first attempt, but should have set event.cancelBubble = true;
Final solution:
Popup.prototype.onAdd = function () {
this.getPanes().floatPane.appendChild(this.containerDiv);
// This handler allows right click events on anchor tags within the popup
var allowAnchorRightClicksHandler = function (e) {
if (e.target.nodeName === "A") {
e.cancelBubble = true;
if (e.stopPropagation) {
e.stopPropagation();
}
}
};
this.contextListener_ = google.maps.event.addDomListener(this.containerDiv, "contextmenu", allowAnchorRightClicksHandler);
};
Popup.prototype.onRemove = function () {
if (this.contextListener_) {
google.maps.event.removeListener(this.contextListener_);
this.contextListener_ = null;
}
if (this.containerDiv.parentElement) {
this.containerDiv.parentElement.removeChild(this.containerDiv);
}
};
see https://developers-dot-devsite-v2-prod.appspot.com/maps/documentation/javascript/examples/overlay-popup for remainder of Popup code

Keep menu from closing when using touch events

I have hooked up a simple long touch function that after 500ms uses the "open" API command to open the context menu. The menu opens. However, on "touchend" the menu disappears. It only stays if I touchmove over the context menu before "touchend". Is there a way to prevent this sort of behaviour? From the source code, only a "touchstart" in a different part of the dom should trigger a close event.
Code is below, in case useful. Not that a delegate of tr is required by my context menu - to explain the targetTr variable use below.
var mobDevice_onLongTouch,
mobDevice_touchTimer,
mobDevice_longPressDuration = 500; //length of time we want the user to touch before we do something
//handle long press on the datatable
var touchArea = document.querySelector("#table");
touchArea.addEventListener("touchstart", touchAreaTouchStart, false);
touchArea.addEventListener("touchend", touchAreaTouchEnd, false);
function touchAreaTouchStart(e) {
var targetTr = $(e.target).closest('tr');
mobDevice_touchTimer = setTimeout(function () { touchArea_onLongTouch(targetTr) }, mobDevice_longPressDuration)
};
function touchAreaTouchEnd(e) {
if (mobDevice_touchTimer) {
clearTimeout(mobDevice_touchTimer) //reset the clock
}
};
function touchArea_onLongTouch(target) {
$('#table').contextmenu('open', target);
};
I solved this. ContextMenu was working fine, but the DOM control I was touching on registered a change event (to highlight a table row) on touchend. So the context menu popped up during touch and hold, then got cleared by a DOM change at touchend.
The solution was to manually add the highlight table row event to touchstart and preventDefault on touchend (when the touch target was inside the table)

Auto refresh a Div without scrolling to the top of Div

I have a script that refreshes a Div on my website every 20 seconds. The problem is, once it refreshes, it scrolls to the to of the Div. I want it to stay at the last position and not scroll to the top after a refresh. Could someone please look at this script and maybe point out what I'm doing wrong? Thanks in advance!
var time = new Date().getTime();
var refreshTime = 20*1000;
$(document).bind("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error hover change", function(e) {
time = new Date().getTime();
});
var lastScrollPos = 0;
$('#feed1').on('scroll', function() {
lastScrollPos = this.scrollTop;
});
function refresh() {
if(new Date().getTime() - time >= refreshTime)
$('#feed1').load(location.href="/dashboard" , function () {
$(this).scrollTop(lastScrollPos);
});
else
setTimeout(refresh, refreshTime);
}
setTimeout(refresh, refreshTime);
Why don't you use feed1 div only for loading your dashboard contents and handle its position using style.
#feed1 {
height: 150px;
overflow: auto;
}
Add other data outside feed1 div because load method will overwrite feed1's content.
See this example if you are looking for something similar otherwise you can modify this example so that other can understand your requirement/scenario.
If you do location.href="/dashboard", the browser will discard all the page's state (including scripting variables) and load "/dashboard" (assigning a value to location.href is identical to calling location.assign("/dashboard")).
The jQuery load function will probably not even require you to reposition the scroll offset, if you gave it chance to execute!
Try:
$('#feed1').load("/dashboard #feed1");
UPDATE:
It seems the HTTP request fired by jQuery's load mechanism is receiving a truncated response in your case (content-length: 0).
You would have to put the scroll position you want to keep into localStorage. Try:
var time = new Date().getTime();
var refreshTime = 20*1000;
$(document).bind("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error hover change", function(e) {
time = new Date().getTime();
});
var previousScrollPos = localStorage.getItem("lastScrollPos");
if(previousScrollPos)
{
$('#feed1').scrollTop(previousScrollPos);
localStorage.removeItem("lastScrollPos");
}
function refresh() {
if(new Date().getTime() - time >= refreshTime)
{
localStorage.setItem("lastScrollPos", $('#feed1').scrollTop());
location.reload();
}
}
setInterval(refresh, refreshTime);
I'm not 100% sure that you want to bind to the scroll event of #feed1 or just of body, I haven't seen your page. If anybody clicks "load more" on the news feed, those additional items will be hidden again since you're reloading the page. Not ideal at all.
Many users will hate having the page periodically reload, it's also an accessibility failure since this will interfere with screen reading software. It might be a simpler idea to just put a refresh newsfeed icon on the page, which just reloads the whole page, when the user wants to (instead of periodically).

Detecting a button that is loaded asynchronously

On a web page I am looking at, there is a lot of data being loaded asynchronously.
Included with that data is a button called "View More". If you either click on the button or simply scroll right to the bottom, more data will be loaded via a "GET" call function.
When I try using this (in FireBug console):
document.getElementById('#button9').click();
or
document.getElementById('button9').click();
I get this result:
TypeError: document.getElementById(...) is null
document.getElementById('#button9').click();
From what I have read about this TypeError, the Id cannot be detected, which I assume is because of the data being asynchronous. Is there a way to detect the element (it doesn't show up on the "page source" but it is there when I click on "Inspect Element with FireBug")?
I would like to detect the element and then make a call to a click event to 'simulate a click' (meaning I would like a click to take place without clicking on the button or scrolling right down) with the output being displayed in the browser (per usual).
If you just want to attach the event, then the bottom will work.
If the problem is that it is null, and you don't know when it will show up, your best bet is to create a loop that runs until it exists.
var obj = document.getElementById('button9');
obj.addEventListener('click', function() {
alert(' you clicked on ' + this);
}, false);
in case its that second instance, and you want to wait until the object events, then use:
checkExists();
function checkExists() {
var obj = document.getElementById('button9');
if (obj != null) {
obj.addEventListener('click', function() {
alert(' you clicked on ' + this);
}, false);
alert('added event');
}
else {
setTimeout(checkExists, 1000);
}
}
This will work, no matter even if you don't know when the object is getting created and added to the DOM
//chrome / ff /IE
$("#button9").bind("DOMSubtreeModified", function() {
// Add click function here
});
//IE 8 and lower
$("#button9").bind("propertychange", function() {
//Add click function here
});
2nd Option
*OR*
Not sure if it supposts IE8 though but it definitely supports all others
document.addEventListener("DOMSubtreeModified", function (e) {
if ($(e.target.id) == "#button9") {
// Do stuff
}
}, false)
Explanation: It checks if the DOM has found an element with the specific ID, if so, execute the click function.

JQuery Mobile "tap" event on button also triggers "tap" on element behind it

I have a JQuery Mobile page in which the content is a list in which the user can tap to select (highlight) elements. After a desired number of list elements are selected, they can be deleted by tapping the Delete button in the footer. The page works fine on my desktop, but in a mobile environment (iPhone & iPad), pressing the delete button also triggers a tap event for the list element underneath the button.
This picture shows how my page normally looks. If the user taps the delete button, the selected element is deleted but the element underneath the delete button will be highlighted.
Why is this happening and what can I do to fix it?
EDIT (some code):
Here is the event mapper:
$(document).delegate("#delete-button", "tap", deleteButtonTapped);
Here is the function:
var deleteButtonTapped = function(event, data) {
event.stopPropagation();
var possessedNotes = [];
$('.ui-btn-up-e').each(function(){
$(this).slideUp();
var id = $(this).attr("id").split(" "); //id is loanId + " " + docId
possessedNotes.push(notesList[[id[1], id[2]]]);
});
// console.log(possessedNotes);
$.post("srv/move_notes_into_possession.php", { possessedNotes: possessedNotes }, function(response) {
console.log(response);
}, "json");
$("#footer").slideUp();
};
I'm maintaining an old app that uses this feature, and I'm seeing the exact same issue happening.
I was able to prevent underlying elements from also being 'tapped' using the event method, preventDefault.
$('#cancelButton').on('tap', function(event) {
event.preventDefault();
// event-handler code
});
Events on DOM elements propagate or "bubble". You need to use jQuery's event.stopPropagation() function in the event handler to stop the event from traveling to the parent elements.
http://api.jquery.com/event.stopPropagation/

Categories

Resources