javascript: prevent page from blocking ctrl+c / ctrl+v - javascript

Some form validators block CTRL+C/CTRL+V on some input fields. Is there a quick bookmarklet or something that could allow me to use my keyboard properly. Or, perhaps, browsers allow my to override that kind of rude behavior of some pages?
EDIT: What I'm looking for is similar solution like the one that can be used to enable right-click on a page by executing a javascript snippet withing context of the page:
javascript:void(document.oncontextmenu=null)
So far, I think that there is no similar solution with javascript snippet without involvment with 3rd party tools like Greasemonkey

That depends on how it is done. Also, removing/disabling event listeners is always risky, you might harm the functionality of the site seriously - and you never know whether a handler does evil or good things, least you can know whether there is a handler at all.
So, I've skimmed the first few Google results for "javascript prevent copy paste" and found mostly foolish scripts: Disable pasting text into HTML form, Prevent Copy, Cut and Paste into HTML Input Text Fields, not working example. So what a script could do now:
remove all on-copy, -paste, -input, -cut, -drag, etc properties of input elements
capture these event types on the document and stop their propagation, so (bubble) handlers will never get called and can't prevent the default action
Yet, as already said, this can't hinder everything. The input event, a very useful one, is cleverly used by this solution to prevent inserting more than one character at once. So you would need to decide whether you want to stop that event and possible break the application.
var evts = ["copy", "paste", "cut", "drag", "selectionstart?", "input?", …?];
var els = [document, document.body].concat(
[].slice.call(document.getElementsByTagName("input")),
[].slice.call(document.getElementsByTagName("textarea"))
);
function trap(e) { e.stopPropagation(); }
for (var i=0; i<evts.length; i++) {
var evt = "on"+evts[i].charAt(0).toUpperCase()+evts[i].substr(1);
for (var j=0; j<els.length; j++)
els[j][evt] = null; // remove handler
document.addEventListener(evts[i], trap, true); // capture phase
}

Another solution on windows: AutoHotkey.
SendRaw, %Clipboard%
For example, the following binds to ctrl+F3 to type out the clipboard contents. Just put this in a file called typeclipboard.ahk and run it with AutoHotkey:
^F3::SendRaw, %Clipboard%

Related

How to display Find dialog in a WebView2 control?

I have got an old C++ MFC app upgraded to use the WebView2 web browser control, based on Edge. Otherwise I have got it working fine, one of the remaining issues is opening the Find dialog in it via the main MFC app Edit-Find menu item or via Ctrl+F (which is also intercepted by MFC framework). What I have now is:
m_webView->ExecuteScript(L"window.find(\"\", 0, 0, 0, 0, 0, 1);");
where m_webView is a pointer to ICoreWebView2 interface.
The problem is that sometimes it does bring up the Find dialog, sometimes not. Sometimes it does it after page refresh, sometimes not. When I close the Find dialog from cross, it typically refuses to open it again.
How can I make it behave properly? Maybe there is a way which does not need to go through Javascript?
Update: it appears it only worked if the Ctrl+F keystroke was somehow sent directly to the WebView2 control, bypassing MFC. It looks like the above Javascript line never worked. So maybe there is a way to simulate Ctrl+F for the WebView2 control?
AFAIK, WebView2 currently has no support for you invoking or otherwise controlling the find in page dialog. You can also refer to this thread. There is a similar thread in GitHub and the official hasn't given a solution.
You can use Ctrl+F keystroke directly in WebView2 control or provide feedback about this issue on WebView2 Feedback page. Thanks for your understanding.
As #yu-zhou answered, there is no official way yet to do what I wanted. For now I resorted back for emulating Ctrl+F programmatically for the WebView2 control, but the result ain't so pretty. For the record I still present it here:
First, the user has to click in the webview2 control at least once, so I can record its HWND in the GotFocus event handler registered by add_GotFocus():
void MyHtmlView::OnGotFocus() {
if (!m_webview2_hwnd) {
m_webview2_hwnd = ::GetFocus();
}
}
Now, when I want to bring up the Find bar programmatically, I need to ensure the correct window is focused and send it the Ctrl+F key. Alas, it appears the control checks the Ctrl key state in async way, so it gets a bit tricky, I need to alter the keyboard state for the thread and add an extra event handler for restoring it afterwards. After some trial and error I reached this (error checks omitted for brevity):
void MyHtmlView::Find() {
if (::GetFocus()!=m_webview2_hwnd) {
::SetFocus(m_webview2_hwnd);
}
std::uint8_t state[256];
::GetKeyboardState(state);
bool restoreCtrlState = false;
if ((state[VK_CONTROL] & 0x80)==0) {
// Ctrl key is currently not pressed.
// Mark it pressed.
state[VK_CONTROL] |= 0x80;
::SetKeyboardState(state);
restoreCtrlState = true;
}
std::thread auxThread([this, restoreCtrlState]() {
::SendMessage(m_webview2_hwnd, WM_KEYDOWN, 'F', 1);
::SendMessage(m_webview2_hwnd, WM_KEYUP, 'F', 1);
if (restoreCtrlState) {
::PostMessage(m_hWnd, WM_COMMAND, ID_RESTORE_CONTROL_STATE, 0);
}
});
auxThread.detach();
}
// This needs to be registered to handle ID_RESTORE_CONTROL_STATE
void HtmlView::OnRestoreCtrlState() {
std::uint8_t state[256];
::GetKeyboardState(state);
if ((state[VK_CONTROL] & 0x80)!=0) {
state[VK_CONTROL] &= ~0x80;
::SetKeyboardState(state);
}
}
Needless to say, this hack may easily cease to work if they change something in the webview2 implementation.

JavaScript onclick event works only as double-click

I'm writing because I need to solve this problem. Until recently, the code was working just fine and I've been using it for quite a long time, but yesterday, when I was testing the page, everything changed.
The idea is click on an image with an anchor tag that is going to redirect the user to another page and in doing so, a confirm dialog box should pop up to ask the person whether they want that. I haven't changed anything in the code, so I'm not getting what's happening. Here's the code:
// **JavaScript**
function confPopUp() {
for (var i = 0; i < 6; i++) {
document.getElementsByClassName("redPic")[i].onclick = redConf;
}
}
function redConf() {
var conf = confirm(
"You're about to be redirected to our social media page. Do you accept?"
);
if (conf) {
return true;
} else {
return false;
}
}
window.onclick = confPopUp;
<!-- **HTML** //THERE ARE 5 MORE ELEMENTS WITH THE CLASS NAME "redPic". -->
<img class="redPic" src="images/instagramLogo.png" alt="Instagram Logo">
The problem is that I'm testing it, right now, and it's not working properly, a the day before yesterday it was working fine, only one click and now, it's working as double-click.
I'd appreciate your help, thanks.
When the page first loads, you're setting a single event handler:
window.onclick=confPopUp;
Later, when there's a click anywhere in the window, that runs your confPopUp function, which hooks up the click handlers on the .redPic elements.
Later, if you click a .redPic element, your redConf function runs.
If you want the .redPic elements to have their handlers hooked up on page load, call confPopUp instead of making it a click handler. Change:
window.onclick=confPopUp;
to
confPopUp();
Be sure this code is running after the .redPic elements exist. There are several ways to do that, pick the one that suits your target browsers and preference:
Put the code in a script tag at the end of the document, just before the closing </body> tag. This works with all browsers, no matter how old.
In even vaguely-modern browsers, call confPopUp from a DOMContentLoaded event handler:
document.addEventListener("DOMContentLoaded", confPopUp);
In modern browsers, add a defer attribute to your script tag.
In modern browsers, add type="module" to your script tag to make your script a module. That defers it just like defer does, and puts it in strict mode (which is a good idea), and put the code in it in module scope rather than global scope (which is really crowded).
So why did it seem to work yesterday? Presumably, because you were clicking the window without realizing it, triggering that initial event handler that hooked up the .redPic elements. Today it just happened that you didn't click the window before trying to click a .redPic element, so you discovered this problem. The problem's been there all along.

Programmatically temporarily altering a Google Docs and then replacing with the pre-altered version

I am trying to do the following, which would seem to be a straightforward task. After 2 weeks of trying various approaches, I am still failing at Step 4:
Starting with an existing Google Doc (Gdoc) file,
programmatically accept all suggested changes,
and then export file as a text file (this step goes quickly),
and then undo the temporary changes so that file is identical to how it started out in Step 1.
My approach (see attached code) was to write a JavaScript (which I have made into a bookmarklet) to:
save content of the original Gdoc (the version in Step 1),
then accept suggested changes (a real life-saver here, btw: http://www.tcg.com/blog/accept-all-bookmark-for-google-docs-suggestions/)
export the file locally as plain text (globally accepting the suggested changes allows those edits to be incorporated into the text!)
restore the document to the saved version.
The Javascript code follows. My question is: why doesn't my approach of saving original file content through sessionStorage.setItem, and then retrieving it to restore the document in the last step, not working? ("Not working" in this context means that when viewing the document after running the script, the "suggested changes" do not go back to being indicated as "suggestions" as in the original document).
In addition to the sessionStorage.setItem approach, I've tried lots of others, including storing document.getElementsByClassName("*stuff*").innerHTML, .outerHTML, .innerTEXT, .outerTEXT in variables inH, outH, inT, outT, respectively, at the beginning of the script and then equating document.getElementsByClassName("*stuff*").innerHTML, outerHTML, innerTEXT, .outerTEXT to inH, outH, resp. Didn't work. ("stuff" is "docos-icon-accept-suggestion-checkmark", btw)
I've also tried making a "backup copy" of document.getElementsByClassName("*stuff*") by cloning it: backupD = document.getElementsByClassName("*stuff*").cloneNode(true) then later restoring by document.getElementsByClassName("*stuff*").replaceWith(backupD) Didn't work, either.
Is it possible that something other than .getElementsByClassName("*stuff*") is what I should be storing then using to restore the Gdoc? I'm using the above node because it is what is used to globally accept the suggested changes, which would seem to be the relevant thing to "undo" the process.
Speaking of "undo", I also looked into trying to programmatically "undo" to restore all of the accepted suggested changes. My code knows how many times that "undo" would need to be evoked (and when I click the undo arrow on the Gdocs menu manually by that many times, I get back the original document), but I could not find anything written about how the "undo" button is implemented in Gdoc and how to programmatically "hit" the undo button.
Finally, the other alternative that I thought of (was my first idea to explore) was to somehow programmatically make a copy of the GDoc on the Google Drive (there's probably lots of guidance out there to do this), then use that copy as my "scratch copy" for accepting all suggested changes so that I didn't have to worry about restoring original content to a file. But the hurdle there is that when I fire-up this javascript, it operates on the file that was opened when I clicked on the bookmarklet, without any obvious way to open up and then "accept all suggested changes" on the copied file.
Here's my code (which fails to restore the Gdoc to its original state). Any advice regarding what approach I should be using to get my Gdoc to the original state it was in before doing a global "accept all suggested changes" would be very much appreciated:
javascript:(function() {sessionStorage.setItem("backup",document.getElementsByClassName("docos-icon-accept-suggestion-checkmark")); var d = document.getElementsByClassName("docos-icon-accept-suggestion-checkmark"); d = Array.prototype.slice.call(d); var cnt = 0; d.forEach(function(n){ var e = document.createEvent("MouseEvents"); e.initEvent("click", true, false); n.dispatchEvent(e,true); e = document.createEvent("MouseEvents"); e.initEvent("mousedown", true, false); n.dispatchEvent(e,true); e = document.createEvent("MouseEvents"); e.initEvent("mouseup", true, false); n.dispatchEvent(e,true); cnt++; }); alert('JUST ACCEPTED ALL SUGGESTED EDITS: ' + cnt.toString()); document.getElementsByClassName("docos-icon-accept-suggestion-checkmark").replaceChild(sessionStorage.setItem("backup")); alert('JUST FINISHED RESTORING TO ORIGINAL FILE');}) ();
Some possibly useful/relevant links:
scripting a google docs form submission (has problem of how the Javascript running from the opened "permanent" file, would be able to open the cloned document -- the script could probably copy and upload the doc without problems, however).
Programmatically edit a Google doc with JavaScript (quite related to my question, but answers implied that one cannot change Gdoc content without Google Apps Script, which I find confusing because I definitely was able to "accept suggested changes" and change the apparent content of the document. I had the same problem with my attempts at Google Apps as I have with the current javascript: I am tied to the opened document and unable to open and manipulate other documents programmatically. Also, I was unable to get Google Apps to execute the script that accepts all suggestions).
How to clone (and restore) a DOM subtree (one of the references from which I got the idea of using .cloneNode, which proved to be another dead end for my project).
The following JavaScript, written as a bookmarklet, achieves all of the above objectives, in case this solution might help others who similarly would like to temporarily "accept all suggested changes" in a Google Doc, and then revert the document back to the original state. The specific steps and how they were accomplished follows, if you have a Google Docs on display at the time that you execute the Bookmarklet given at the bottom of this answer.
Programmatically accept all suggested changes. (Algorithm from http://www.tcg.com/blog/accept-all-bookmark-for-google-docs-suggestions/):
var d=document.getElementsByClassName("docos-accept-suggestion"); d = Array.prototype.slice.call(d); d.forEach(function(n){ var e = document.createEvent("MouseEvents"); e.initEvent("click", true, false); n.dispatchEvent(e,true); e = document.createEvent("MouseEvents"); e.initEvent("mousedown", true, false); n.dispatchEvent(e,true); e = document.createEvent("MouseEvents"); e.initEvent("mouseup", true, false); n.dispatchEvent(e,true); });
Programmatically export plain text (which should now have suggested changes incorporated):
window.open(window.location.href.replace("edit","export\?format=txt"));
Do stuff with the Google Doc and/or with the exported plain text file (for example, my files had raw LaTeX, so I compiled the file and got a pdf of my research paper).
Switch the Doc back to its original, pre-accept_all_suggested_changes state.
w=window.open(window.location.href.replace("edit","copy"));w.addEventListener('load',w.close(),true);alert('Check your download folder for the text file');setTimeout(function(){var a=document.getElementsByClassName("docs-icon-undo")[0]; for(var j=0; j<cnt; j++){e=document.createEvent("MouseEvents"); e.initEvent("click",true,false);a.dispatchEvent(e,true);e=document.createEvent("MouseEvents");e.initEvent("mousedown",true,false); a.dispatchEvent(e,true);e=document.createEvent("MouseEvents");e.initEvent("mouseup",true,false);a.dispatchEvent(e,true)}}
(where cnt is the number of suggested changes that were accepted in the previous step. The code figures out the value of cnt for you.)
Altogether, the functional bookmarklet is as follows (where "[DO STUFF]" is where you put in whatever code you want that utilizes the Google Doc or plain text file with all suggested changes incorporated):
javascript:(function(){var d=document.getElementsByClassName("docos-accept-suggestion"); d = Array.prototype.slice.call(d); d.forEach(function(n){ var e = document.createEvent("MouseEvents"); e.initEvent("click", true, false); n.dispatchEvent(e,true); e = document.createEvent("MouseEvents"); e.initEvent("mousedown", true, false); n.dispatchEvent(e,true); e = document.createEvent("MouseEvents"); e.initEvent("mouseup", true, false); n.dispatchEvent(e,true); });window.open(window.location.href.replace("edit","export\?format=txt")); [*DO STUFF*]; w=window.open(window.location.href.replace("edit","copy"));w.addEventListener('load',w.close(),true);alert('Check your download folder for the text file');setTimeout(function(){var a=document.getElementsByClassName("docs-icon-undo")[0];for(var j=0; j<cnt; j++){e=document.createEvent("MouseEvents"); e.initEvent("click",true,false);a.dispatchEvent(e,true);e=document.createEvent("MouseEvents");e.initEvent("mousedown",true,false); a.dispatchEvent(e,true);e=document.createEvent("MouseEvents");e.initEvent("mouseup",true,false);a.dispatchEvent(e,true)}},5000);})();
The code has been tested on a Chrome browser.
Google docs (in Chrome) now has a tool that allows previewing the document with all suggested edits applied:
Tools --> Review suggested edits --> Preview "accept all"
While exporting the document does not reflect the previewing, the document can be printed and copy-pasted with the suggested edits applied so works for the purpose of obtaining a suggested-edits-applied plain-text or pdf version of the document.

Google Analytics does not track link click events

I am currently using analytics.js (the newer version of GA) and I am trying to track all types of events from my website, including when a user clicks on an anchor tag pointing to an external URL.
I am currently using this setup:
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-KEY-HERE', { 'alwaysSendReferrer': true, 'allowAnchor': true });
and I send the events when hitting a link like this:
$(document).on("click", ".anchor-class", function (event) {
label = //get data from anchor element here ...
ga('send', 'event', 'Link Clicked', 'Click Details', label);;
return;
}
});
and this does not send anything to GA (even though the event handler is calling the ga(send...) method).
How ever, if I use this exact technique for but with event.preventDefault(); at the beginning of the function, the event is sent and it appears in GA dashboard.
Is there some setting that I missed in order to make this work properly?
Use the hitCallback function:
$(document).on('click','a', function(event){
event.preventDefault();
var label = $(this).attr('href');
ga('send', 'event', 'Link Clicked', 'Click Details', label, {
'hitCallback': function(){
window.location.href = label;
}
});
});
As pointed out by Blexy, the correct way to do this is to use a hit callback. However, you also need to take into account that users may block Google Analytics using some privacy protection tool such as Ghostery, in which case the hit callback will never be executed. The following article explains how to implement this correctly:
http://veithen.github.io/2015/01/24/outbound-link-tracking.html
We currently had this issue and ported our analytics code off of the website and into GTM. Another issue is that we have hundreds of sites that cannot have the new code released to them to deprecate the on-page analytics, but we already had GTM on them.
We were able to find the jQuery events that were bound to the click event and write code in GTM that removes the jQuery event on those clicked buttons via the exact event handler. Then we were able to apply standard GTM click triggers and tags so we didn't get double eventing.
Assuming you can easily remove the code from the page the following should work great using GTM.
This will fire an event off to analytics when a user clicks a given element and it will make any navigation wait until the corresponding tags have finished firing first before letting the page navigate away.
GTM Implementation
This is the quick and standard way now with the newer GTM.
Pre-Setup
Variables
You will need access to the Click Element Built-In variable. You can enable this under Variables --> Configure --> Click Element.
Also enable the Page Path or Page URL Built-In variable. This is later used to help you determine which pages to run the trigger on.
It looks like you're trying to get some text off of the clicked element for the event. If so you should create a Custom JavaScript variable. This just gets the inner text of the element, but you could also get an attribute or whatever other data you're looking for at this step.
Name: Click Element - Inner Text
Variable Type: Custom JavaScript
Custom JavaScript: function() { return {{Click Element}}.innerText; }
Triggers
Create a new trigger
Trigger Type: Click - Just Links
Wait for Tags: Enable this, this is the magic you're looking for.
Max wait time: set this to what you feel is appropriate and if you can live with the possibility that you may loose some analytics for a better user experience. Imagine what the wait time may be for a 3G user on a mobile device. Is 5000ms enough, not enough, maybe 10000ms. The user probably understands their connection is bad.
Enable this trigger when all of these conditions are true:
NOTE: you should only run this on pages that you need to run it on. Google isn't very clear if there is a performance loss and their "Learn More" says nothing about it.
If you need this to run on all pages though, configure it like so:
Page Path matches RegEx .*
Otherwise you should write something like:
Page Path matches RegEx ^/path/my-page$ specific page
Page Path matches RegEx ^/path/my-page/.*
NOTE: I'm not sure if those regex are correct, I'm not sure if you'll get a path with a proceeding or appended forward slash / or if you need anchors - normally it's best to be explicit on anchors so you don't get any funny business.
This trigger fires on: Some Link Clicks
Fire this trigger when an Event occurs and all of these conditions are true. Choose one below that fits your needs and change it how you need it.
Click Element matches CSS selector a
or maybe something more specific?
Click Element matches CSS selector .container .calls-to-action a
maybe only on external links? Assuming all internal links are relevant pathing.
Click Element matches CSS selector a[href^="http"]
Tags
Create a new tag
Tag Type: Event
Category: Link Clicked
Action: Click Details
Label: {{Click Element - Inner Text}}
Google Analytics Settings: best to use these for reusability and consistency rather than manually setting them up.
Firing Triggers: The Link Clicking Trigger you created above.

How to get first pick at keydown (preventing "add bookmark" dialog from showing)

I have a checkbox in my jQuery application that you can check to duplicate a <div>. I'd like now to create a Ctrl+D shortcut for the operation. I can sense Ctrl+D with:
$(document).on('keydown',function(e) {
debugger;
if((e.keyCode == 68) && e.ctrlKey){
$('.duplicate').trigger('click');
}
});
But it looks like Firefox is capturing the Ctrl+D first and putting up a dialog to "Edit a bookmark."
How can I get the interrupt first, and then I'll kill it when I'm done? I don't want my users to have to dismiss a Firefox dialog every time they enter Ctrl+D.
Try using e.preventDefault();:
$(document).on('keydown',function(e) {
//debugger;
if((e.keyCode == 68) && e.ctrlKey){
$('.duplicate').trigger('click');
e.preventDefault();
}
});
See demo page here (edit the demo here).
On a side note, it is not a good usability practice to override widely-known keys/commands. While you can achieve that, it is advisable and better to use another combination of keys.
Have you tried e.preventDefault();?
Or a better alternative - pick a different combination. Users typically don't like it when you start messing with their key bindings. On many sites (trello and github come to mind), there are no control keys at all. You can invoke special functionality just by typing a single character (on github "t" opens a page where you can type a filename and it will filter files in your repository). And in most cases that should be fine. You don't typically need to be able to type things on the html body. You would just want to make sure they're not inside an input or textarea before you fire the functionality.

Categories

Resources