canvas keyboard focus through iframe (resists tab-switch) - javascript

I have a canvas in iframe.html which gets loaded in index.html. Within iframe.html, the game gets keyboard input via window.addEventListener('keypress', press, false);.
This works great. However, if at any time the user presses tab, the iframe loses focus, and can't get it back (even if you click back on the iframe/canvas). Mouse input continues to work- but keyboard is ignored.
One solution I've found is putting this in index.html:
setInterval(function(){document.getElementById("content").contentWindow.focus();},100);
("content" is the id of the iframe).
The problem with this is: 1. it's constantly running this BS code, and 2. I may-or-may-not have control of what goes in index.html in the future (I might be hosting this on third party sites).
So I assume there's a solution that doesn't constantly execute re-focusing code, and doesn't require any code within the iframe-containing web page.
Note- I'm totally fine if "tab" loses focus- but there needs to be a way for the user to re-give the iframe focus.

Within the iframe document, can you add an event listener on the document that calls event.preventDefault(); on keydown? Also, in the same way, could you add a click event listener that calls focus() on whatever you want focused? If it's not a naturally focusable element, add tabindex="1".
There may be some feature of iframes that I'm not considering, but that seems like it should work.
document.querySelector('div').addEventListener('click', ()=>{
document.querySelector('div').focus();
});
document.querySelector('div').addEventListener('keydown', (e)=>{
if (e.key === 'Tab') {
e.preventDefault();
}
});
div {
background-color: red;
}
div:focus {
background-color: blue;
}
<div tabindex="0">I'm bad for accessibility!</tabindex>

Related

How to grab some mouse events, send others to iframe?

I need to capture certain user events (i.e. double-click) but let the rest pass to the iframe below. Some of these events may be enabled/disabled over time. I also don't want to block the iframe from receiving simple events like click or scroll. It seems, however, that iframe gets dibs on both bubble events (makes sense) and capture (this doesn't make sense, as it violates the order of propagation).
It seems like the only way to prevent the iframe from stealing all events is by putting an invisible div above it. In that case, however, I'd need to write handlers for all events to create a fake fall-through to the iframe, because even the events the div doesn't capture will no longer hit the iframe.
I see the following potential problems with this approach:
I may not be able to pass-through/simulate a click event into a foreign iframe (most iframes would be generated via srcdoc, so they'd be local, but some may reference foreign location via src)
I will need to write handlers for just about every mouse event to simulate a pass-through
It may be problematic to send the event to iframe itself and let it resolve the coordinates rather than detecting which element inside the iframe should receive the event
I may be wrong about my assumptions, so feel free to correct me.
Another approach I played around with involves detecting when iframe gets focus:
function clickListener() {
var monitor = setInterval(function(){
var elem = document.activeElement;
if(elem && elem.tagName == 'IFRAME'){
message.innerHTML = 'Clicked';
setInterval(function() {
message.innerHTML = '';
}, 100);
clearInterval(monitor);
elem.blur();
clickListener();
}
}, 100);
}
clickListener();
iframe {
width: 500px;
height: 300px;
}
<iframe id="iframe" src="//example.com"></iframe>
<div id="message"></div>
Problems with this approach:
the 100ms loop isn't ideal when I have 20+ elements on the page doing this
it ignores hover events and lumps all click-like events into a click
These two problems (especially the 2nd) are actually pretty severe show-stoppers, as I want to be able to detect double-click and drag events as well.
Does anyone have suggestions for how to tackle this?
You can check for the click coordinates on the fly and see if they match to your iframe. If they do, then its a double click on iframe, else, let other elements handle the event.
document.addEventListener("dblclick", check);
function check(event) {
if(document.elementFromPoint(event.clientX, event.clientY).id == "message") {
alert('iframe clicked');
} else {
alert('something else dude');
}
}
And obviously, stop iframe from receiving any clicks:
iframe {
pointer-events: none;
}
Here's a fiddle: https://jsfiddle.net/rg59yaau/1/

Can't click button when soft-keyboard is visible

When an text input element has focus (and the soft keyboard is visible), I can't click the submit button (which is clearly visible) directly. I have to first dismiss the keyboard (by clicking elsewhere or on "Done" on the keyboard), and then click the submit button.
This is confusing behaviour. I have considered removing the log-in button altogether when the keyboard is visible, but that would be too confusing (for users wouldn't know that they could click "Go", or will be annoyed that the login button keeps disappearing).
This only happens on iOS (not sure if its restricted to iOS 7, but suspect it might be), and I'm using Telirik AppBuilder for my app.
function isTextInput(node) {
return ['INPUT', 'TEXTAREA'].indexOf(node.nodeName) !== -1;
}
document.addEventListener('touchstart', function(e) {
if (!isTextInput(e.target) && isTextInput(document.activeElement)) {
document.activeElement.blur();
}
}, false);
Because I was using <button> tag the soft keyboard popped up when not required. I have now changed all for <table> (inside a <td> tag, but could be a <div> ) with an <img> tag inside for the icons. To make the table look more like a button there is a CSS class for colours/borders. A submit area is right at the top (too).
The page can be inspected at http://ask.stroudvoices.co.uk/
It would seem that Android (assume iPad etc also) treat buttons as another kind of <input>, whereas <table> is regarded as <body>.
Sometimes there is a simple answer, from old technology!
I meet a similar problem.What my need is when I tap the submit button and I want to keep the soft-keyboard visible.
Here is my solution.
submit.addEventListener('mousedown', function(e) {
e.preventDefault();
});
I give the submit button a mousedown event, and prevent the default event. it's work on most mobile phones.but in some system found ios 12.1.2 is not available.it's just like the question's owner says, i need to tap twice, one to hide the keyboard, second to reach the submit button.
This confused me and I can't solve it.

JavaScript Click event fires via the Enter Key but not via the Mouse

Some code that looks like the following is firing the click event via the Enter key, but is not responding to the mouse click.
//a is an anchor element
a.addEventListener('click', function (e)
{
//Do Stuff...
});
This page demonstrates the problem. The relevant bit of code is at line 176. This is in the middle of development and currently only (sort of) works in Chrome.
Also, I just verified that it works if I use mousedown, so it's not just the case of an invisible element sitting in front of the anchor.
Any ideas?
Edit: Now that you've shown us the actual code you're using, the problem is related to the fact that the autoSuggest() function has it's own click handler and in that click handler, it is clearing the container which removes all <a> elements in the container so your link object gets destroyed (probably before your click event gets to process). So, you can get events that happen before the click (like mousedown), but after a click, the element is removed from the DOM.
If you tell us what you're trying to actually do when an auto-suggest item is clicked that is different than the default behavior of the autoSuggest() function and you point to any documentation for that function, then perhaps we could offer a better way to solve your issue.
The link may be firing and taking you off to a new page (or reloading the current page), thus preventing you from seeing the click code run. Usually when you process a click event on a link element, you need to prevent the default behavior:
//a is an anchor element
a.addEventListener('click', function (e) {
e.preventDefault();
//Do Stuff...
});
Another possibility is that you are trying to install the event handler too soon either before the DOM has been loaded or before this particular link has been created and thus no actual click event handler is attached to the DOM object. You can verify whether the event handler is even getting called by temporarily putting an alert("Click handler called"); in the event handler and see if that pops up or not.

Capture click on div surrounding an iframe

How can I capture a click or mousedown event on a div surrounding an iframe. I've tried attaching the function to click event on the div but since the iframe never bubbles the event up to the surrounding div the function is never called. Is there a way I can capture the event on the div and then propagate it to the iframe for default action?
If the click is in the iframe area, the iframe context handles the click event, it does not bubble up to the iframe parent. So the div will never register the click event at all if it happened in the iframe area.
Furthermore, if the iframe contains a page that does not belong to the same domain as the iframe parent, any interaction is prohibited (re. same origin policy).
When the same origin policy is met, there are a few things you can do, you could call a method in the iframe parent context:
top.parentFunction();
So in the iframe you add an event listener that delegates to the iframe parent (accessible with the top reference.
Propagating events is a lot more complicated, so I'm simply going to refer to Diego Perini's NWEvents library. I believe his event system to be one of the better ones out there and he's particular on iframe interaction.
I certainly would not start writing your own code to achieve this, this can easily be a year long project if you want to do it properly and even then will be inferior to Diego's work.
There's no "good" way to do it, but if you really need to detect a click on an Iframe, you can kind-of do it in the latest browsers.
<iframe src="http://mtw-ed.com/" id="iframe" style=""></iframe>
<script type="text/javascript">
var inIframe = false;
function checkClick() {
if (document.activeElement
&& document.activeElement === document.getElementById("iframe")) {
if (inIframe == false) {
alert("iframe click");
inIframe = true;
}
} else
inIframe = false;
}
setInterval(checkClick, 200);
</script>
This script will check every 200ms whether the user is in the Iframe. Of course, they may not have clicked on the Iframe to get there, but I'm afraid this is the best you can do without #BGerrissen's solution.
It will detect the first 'click' only, unless you click out again. It only works in really modern browsers.
You can use a library like porthole to pass messages between parent and iframe, even across domains. Using this wouldn't exactly propagate the event (you won't be able to get the event object), but you can create your own event in the form of a simple message, and then handle it in the parent as a click.
Here's their example
However, I've used Brendon's answer as it's simpler works for my current need.
If you land here because you need to track a click on a PayPal button (like me), and you have access to the JavaScript SDK, you can listen to the click by adding the onClick callback in the initialization.
Example:
paypal.Buttons({
onClick() {
// here you can track the click
}
}).render('#paypal-container');
Link to the docs: https://developer.paypal.com/sdk/js/reference/#link-oninitonclick.

Mobile Safari Event Issue

I have designed a website with a menu that is initially invisible. When the user clicks on a button, the menu becomes visible. There are two ways for the user to hide the now visible menu:
Click the button that caused the menu to become visible
Click anywhere on the web page that isn't the menu
The way I have coded the second option is to tie an onclick event to the window element, and have it compare where the user clicked to the menu's position to determine if the menu should be hidden. This works great in Firefox and Safari, but it fails in Mobile Safari.
I noticed that the window onclick event only fires when I click on another element with an onclick event already assigned. If I click on an element with no event(s) assigned, the window's onclick event never fires. If I click on the button which displays the menu, it fires along with the event tied to the button.
Is it possible to assign events to the window element in Mobile Safari?
I'v been encountering this same problem. Here is what worked for me. (Note: I am working within a Modernizr and jQuery context)
First, I add a custom Modernizr class using Modernizr's addTest Plugin API to test for iOS, which will add the class appleios or no-appleios accordingly.
Because in my research the body seems to fire events on it's own agenda, I am taking a little precaution by wrapping all the document's content with an element in an iOS context. Then I add an event handler to this element.
$(".appleios body").wrapInner('<div id="appleios-helper" />');
$("#appleios-helper").bind("mouseup", function(){return;});
What was suggested earlier in this thread is using void(0). I did some quick testing, and found that void(0) as the event just wasn't causing touches on the body to be recognized. When I plugged in my own "empty" function in the form of function(){return;} things started working.
This all hinges on the fact that no events are fired in Mobile Safari unless the element explicitly has events to fire (Safari Web Content Guide.) By inserting this empty event on the wrapper, things will bubble up to the body.
If you're doing strait JavaScript with none of these libraries, the same effect could be achieved in the HTML markup
<html>
...
<body>
<div id="appleios-helper" onmouseup="function(){return;}">
...
</div>
</body>
</html>
This worked for me to hide tooltips when touching anywhere on the document's body. Your mileage may vary.
Simply adding the dummy onclick handler to the html body works for me:
<body onclick="void(0)">
Note that I am using usual live event handlers as shown below:
function liveHandler( event ) {
var target = event.target; ...}
window.addEventListener(evtype, liveHandler, true);
// evtype such as 'mousedown' or 'click'
// we use the capturing mode here (third parameter true)
This is an old question, but I struggled with the same thing today.
I found that using touchstart event works.
I solved it like this:
var isTouchDevice = 'ontouchstart' in document.documentElement;
if (isTouchDevice) {
// Do touch related stuff
$(document).on('touchstart', function (event) {
// Do stuff
});
} else {
// Do non-touch related stuff
$(document).on('click', function () {
// Do stuff
});
}
You could just add onclick="void(0);" to some <div> that covers the whole page so that no matter what, you are always clicking on an element that has an onclick event. Not a great solution, though.
I'd prefer not having the onclick event be tied to the window. Why don't you create a container <div> that has that event on it. Then handle it just like you currently are.
You can also:
$('body').css('cursor', 'pointer');
No idea what those "engineers" at Apple are doing. LOL.
This has problems though. You wouldn't want to do this on every touch device. Only touch devices that don't also have a pointing device (Laptops with Touch Screens, for example).
Source: http://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
The conclusion of the article is this:
So I don’t understand why all this is the case, but it most certainly is the case. If you’re having bubbling problems, just add an empty-function event handler anywhere between the body and the element, and you’re set to go. But it shouldn’t be necessary.

Categories

Resources