I'm making an extension for Chrome to be used on Youtube. The extension adds an overlay on top of the video with a text input. However, typing into the extension triggers Youtube's various keystroke listeners (e.g. space -> pause). event.stopPropagation() does nothing, neither does return false at the end of the Angular event callback.
My current (successfully prototyped but not yet implemented) solution is to wrap the input in an iframe, which will pass messages back to the parent window:
iframe.contentWindow.document.querySelector("#wrapped-input").addEventListener("input", function(){
result.innerHTML = this.value;
});
I feel that this solution is a bit of a hack, and I'm hoping to find something more elegant. As an aside, I am using AngularJS for the app, so if there are any Angular-specific workarounds, I'd love to know those too. Thoughts?
EDIT
Current solution:
<iframe id="wrapper-frame"></iframe>
...
link: {
var input = '<input id="inner-input" />';
var wrap = $window.document.querySelector('#wrapper-iframe').contentWindow.document;
$scope.commentInput = wrap.querySelector('#inner-input');
wrap.open();
wrap.write(input);
wrap.close();
$scope.commentInput.addEventListener('input', function(){
var val = this.value;
$scope.$applyAsync(function(){
$scope.inputContent = val;
});
});
}
It works, but still -- iframe. Bleh, hack. I will leave the question open in case if someone has a better idea.
Related
I'm currently trying to automate a game called Lyrics Training (https://www.lyricstraining.com/) and I am able to get the words that are from the button through some other code but I am currently struggling with clicking the "button" because it is for some reason classified as a in the HTML code. I was wondering whether there was a way or a function that would allow me to click it so I could finish the automation? Thanks!
So far I have this code that would work if the button was an actual button:
let firstChoice = document.getElementsByClassName("slot s1")[0];
let secondChoice = document.getElementsByClassName("slot s2")[0];
let thirdChoice = document.getElementsByClassName("slot s3")[0];
let fourthChoice = document.getElementsByClassName("slot s4")[0];
// the click function isn't working
for(let i = 0; i < click_order.length; i++){
let word = click_order[i];
if(firstChoice.innerHTML === word){
firstChoice.click();
}else if(secondChoice.innerHTML === word){
secondChoice.click();
}else if(thirdChoice.innerHTML === word){
thirdChoice.click();
}else if(fourthChoice.innerHTML === word){
fourthChoice.click();
}
}
This is how the "buttons" look like in the HTML code of the website:
Any help would be appreciated! Thanks!
If I am not misinterpreting anything, then it sounds like you are trying to automate a game on some website. I'm not sure exactly how they coded the game, but there is a chance the code doesn't actually listen for "click" events.
It seems like your code only works if the website's code responds to click events such as:
div.addEventListener("click", somefunction);
//or
btn.addEventListener("click", somefunction);
In this case, your code would still work on divs.
However, they could be responding to mousedown events:
div.addEventListener("mousedown", somefunction);
If this is the case, you might want to read up on invoking specific events: https://developer.mozilla.org/en-US/docs/Web/Events/Creating_and_triggering_events
But please keep in mind that many websites have code preventing this stuff.
Give the button div element an attribute of role="button". This should work fine.
My problem is I need to capture a keypress but at any given time the user can be inside of an iframe inside of an iframe inside of an iframe.
At first, I thought I could just put the listener on document but that doesn't work if the user is inside of one of those iframes.
Next I thought of attaching the handler to window but ie doesn't support attaching event handlers to window. I'm not even sure if that would work though or it would be the same problem as with document.
Next, I thought I could just go through all the iframes and add individual handlers there but eventually realized that wouldn't work because the iframe doesn't have any html in the dom so there is no way to access iframes nested in it.
One other possible solution is to go to the js of all the iframes and add this code manually but that is way too extreme.
Any other ideas?
I keep answering my own questions but oh well. If you come across this problem I figured out the answer. This uses jquery but you could port it to straight js if you had to.
var myFunction = function () {
alert("hello world");
};
var $myWindow = $(myWindow /*dom object*/);
$myWindow.bind("mouseover", myFunction);
var getNested_iframes = function (document_element) {
$.each(document_element.find("iframe"), function () {
var iframeDocument = $(this).get(0).contentWindow.document; // may need to change this depending on browser
iframeDocument.onkeyup = myFunction;
getNested_iframes($(iframeDocument));
});
}
getNested_iframes($myWindow);
I have a Firefox extension that detects whenever a page loads in the browser and returns its window and document. I want to attach some events (that launch functions in my addon's overlay) to elements in the page, but I don't know how to do this in a way that's safe.
Here's a code sample:
var myExt = {
onInit: function(){
var appcontent = document.getElementById("appcontent");
if(appcontent){
appcontent.addEventListener("DOMContentLoaded", this.onPageLoad, true);
}
},
onPageLoad: function(e){
var doc = e.originalTarget;
var win = doc.defaultView;
doc.getElementById("search").focus = function(){
/* ... 'Some privelliged code here' - unsafe? ... */
};
}
};
So can anyone tell me what's the safe way to add these events/interact with the page's DOM?
Thanks in advance!
I think that you want to listen to the focus event, not replace the focus() function:
doc.getElementById("search").addEventListener("focus", function(event)
{
if (!event.isTrusted)
return;
...
}, false);
Usually, there is fairly little that can go wrong here because you are not accessing the page directly - there is already a security layer (which is also why replacing the focus() method will have no effect). You can also make sure that you only act on "real" events and not events that have been generated by the webpage, you check event.isTrusted for that like in the example code. But as long as you don't unwrap objects or run code that you got from the website, you should be safe.
I have one asp.net application, in which i have to disable or make it as read only to the Paste option from the context menu. But i searched in internet. I didn't get one exact method. Please help me for resolve this issue. Thanks in advance
The short answer is that you can't do this.
The user's browser is their own, and as such they have the sovereign power to interact with your page however they want. You can catch right-click events to prevent the menu from coming up; you can catch keypress events to stop Ctrl-V (and Shift-Insert, which is often forgotten) from being registered. However, you can't remove the Edit -> Paste menu option, which works within the browser and subverts Javascript altogether.
Not to mention that the user could just disable Javascript temporarily, paste into the field, then reenable Javascript if they wanted.
Even if you're happy with the limitations, you cannot modify the actual context menu, only prevent the right click from registering at all.
I suppose isn't possible to disable a single item on context menu, so I think you should try to disable the whole context menu, e.g.
$(document).bind("contextmenu", function(e) {
e.preventDefault();
});
∎ Another way of solving this pesky problem,
using JavaScript's Object-Oriented characteristics
<div class="ro-editable">
<span.....</...
<pre....</...
</div>
<script>
function make_editable_readonly(element){
element.backup_innerHTML = element.innerHTML; /* backup before editable. */
element.contenteditable = "true"; /* editable. CTRL+A is usable (FUN!) */
element.onautocomplete /* what in the world could change the content? actually lot!.. */
= element.onchange
= element.oncontextmenu
= element.oncopy
= element.oncuechange
= element.oncut
= element.ondrag
= element.ondragend
= element.ondrop
= element.onemptied
= element.onkeyup
= element.onmouseup
= element.onpaste
= element.onreset = function restore(){
if (element.innerHTML !== element.backup_innerHTML)
element.innerHTML = element.backup_innerHTML;
};
}
make_editable_readonly(
document.querySelector('div.ro-editable')
);
</script>
∎ Easy! Right? (and with no 💩y key-event "filtering" ☺):
∎ And here is a JSFiddle to play⚽ with
∎ taken from iCompile - ContentEditable And "ReadOnly"? YES! ☕
There is a tree menu in my application and on click of the menu items, it loads a url in a iFrame. I like to set the focus in an element of the page loaded in the iFrame.
I'm using this code, and it works perfectly in all the browsers except IE:
var myIFrame = $("#iframeName");
myIFrame.focus();
myIFrame.contents().find('#inputName').focus();
I have tried all different options like using setTimeout, but no chance.
After the page loads, when I hit the tab key, it goes to the second input, which means it's been on the first input, but it doesn't show the cursor!
I am using ExtJS and the ManagedIFrame plugin.
Any help is appreciated.
You need to call the focus() method of the iframe's window object, not the iframe element. I'm no expert in either jQuery or ExtJS, so my example below uses neither.
function focusIframe(iframeEl) {
if (iframeEl.contentWindow) {
iframeEl.contentWindow.focus();
} else if (iframeEl.contentDocument && iframeEl.contentDocument.documentElement) {
// For old versions of Safari
iframeEl.contentDocument.documentElement.focus();
}
}
Is the iFrame visible onload, or shown later? The elements are created in different order which is the basis of the setTimeout approach. Did you try a high value wait time on a set timeout?
Try something like at least a half second to test...IE tends to do things in a different order, so a high timeout may be needed to get it not to fire until render/paint finishes:
$(function(){
setTimeout(function() {
var myIFrame = $("#iframeName");
myIFrame.focus();
myIFrame.contents().find('#inputName').focus();
}, 500);
});
Difficult to troubleshoot without a working example, but you might try hiding and showing the input as well, to force IE to redraw the element.
Something like:
var myIFrame = $("#iframeName");
myIFrame.focus();
myIFrame.contents().find('#inputName').hide();
var x = 1;
myIFrame.contents().find('#inputName').show().focus();
This might jolt IE into displaying the cursor.
I could get IE to focus an input field in an iframe with:
iframe.focus();
var input = iframe...
input.focus();
iframe.contentWindow.document.body.focus();
input.focus();