Capture Document-level Paste event without focused Input or Textarea - javascript

<!DOCTYPE html>
<html>
<head>
<title>Clipboard Paste Text</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
</head>
<body>
<input type="text" placeholder="paste in here" />
<script type="text/javascript">
/* <![CDATA[ */
$(document, 'input[type="text"]').on('paste', function(event) {
var oEvent = event.originalEvent;
oEvent.preventDefault();
var clipText = '';
if(window.clipboardData){
clipText = window.clipboardData.getData('Text');
}else if(typeof oEvent == 'object' && oEvent.clipboardData){
clipText = oEvent.clipboardData.getData('text/plain');
}
// console.log('Pasted ' + clipText.length + ' characters.');
alert('Pasted ' + clipText.length + ' characters.');
});
/* ]]> */
</script>
</body>
</html>
^ I have this demo code. It binds the paste event on INPUT[TEXT] and DOCUMENT.
In Google Chrome (and Opera 15+), a Ctrl+V with no caret (outside input and textarea) is captured.
In IE and Firefox, a Ctrl+V outside a paste-able object (input and textarea) is not captured.(but binding the document paste event captures paste event for all inputs and textareas.)
Is this proper behavior? Is my JS correct?
I'd like to capture Ctrl+V without a input textbox in all three browsers. I'm using a text input now but I'd like to remove it completely and capture the event at document level, not at input box level. Can it be done?
PS: I need to paste large amounts of text that hog the browser if pasted in a textarea. I'm storing it in a hidden field by capturing the paste event in a inputbox. My current solution works properly but I'm still wondering if I'm missing something or FF and IE will only trigger paste events at input/textarea level.
PPS: I've already used the spellcheck=false and autocomplete=off trick to allow more text pasted... but it still hangs for a bit, and as I don't need it editable, this is a better solution.
PPS: My JS skills are rather rusty (they are more like a JS survival mode) and I have no worries for browser backward compatibility as those who'll use this update often and hard.
Made a jsfiddle: http://jsfiddle.net/ninty9notout/A42UN/

From http://www.w3.org/TR/clipboard-apis/
5.1.3 paste event
The paste event has no default action in a non-editable context, but the event fires regardless.
Chrome uses a lazy paste event, which means that it doesn't check to see if the focused element is an editable content area, which is why the paste event works on the document.
As for firefox and IE, it actually checks the element before letting allowing the paste event to be fired. So basically, you need an content editable element in order for the paste event to work in all browsers.
I played around with using a content editable DIV as the main div on the page and I think it seems to produce the results you are looking for. Forgive me if the answer seems somewhat "hackish".
You can place a contenteditable div as the container div to the rest of your webpage and not allow a user to type into the div by returning false when the user presses a key, unless it's a key for pasting.
Here is a fiddle if you want to see how I did it.
http://jsfiddle.net/NVqQ7/3/
html
<div id="pageDiv" contenteditable><div>other content</div></div>
css
html, body, #pageDiv {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
javascript:
$(document).on('paste', function(event){
var oEvent = event.originalEvent;
if (event.preventDefault())
event.preventDefault();
var clipText = '';
if(window.clipboardData){
clipText = window.clipboardData.getData('Text');
}else if(typeof oEvent == 'object' && oEvent.clipboardData){
clipText = oEvent.clipboardData.getData('text/plain');
}
// console.log('Pasted ' + clipText.length + ' characters.');
alert('Pasted ' + clipText.length + ' characters.');
});
$('#pageDiv').keydown(function(e){
if (!event.ctrlKey)
return false;
});
A few notes:
The user still must click on the body of the page in order to activate the paste event. i.e. clicking on the browser by itself may not be enough.
In firfox, you need to set the contenteditable="false" in child divs, otherwise users can add text to those elements.
I had to make the div height and width 100%. If your html and body are not 100% height and width, it will not work in IE.
I had to change the jquery library to a newer version for IE in order for the paste event to work on the document.
Hope this helps

Related

Javascript CTRL-C is triggered in <textarea>

Sorry, I am not a very best Javascript-programmer, but I am making a homepage for my website ( http://coins.kalf999.com/menu.php) and I was producing some code, but I was suprised it did not work untill....now!
But I still not understand why....
Who can help me..?
I made some small code to show the difference.
In the 1st textarea I can copy some text, press CTRL-C, which hides the textarea and you can test the contents of the clipboard in the 2nd textarea. I use a 1 millescond setTimeout-function, which works !
My old code was the second example:
In the 3rd textarea I can copy some text again, press CTRL-C, which hides the textarea and test the contents of the clipboard in the 4rd textarea.
I not use setTimeout-function, which , obviously, not works !
I can not imagine that a "hidden" object is creating a malfunction for a Copy-command.....
What happens over here...? ;(
I made test-program called tst.html and tst.js
<html>
<head>
<script type="text/javascript" src="tst.js"></script>
</head>
<body>
<div id = "box1"></div>
<div id = "testarea1"></div>
<div id = "box2"></div>
<div id = "testarea2"></div>
<script>
document.getElementById("box1").innerHTML= '<textarea rows="4" onkeypress="CTRLC1(event)">Select some text and copy with CTRL-C.The <textarea> becomes hidden.</textarea>';
document.getElementById("testarea1").innerHTML= '<textarea rows="4" ">Paste text with CTRL-V</textarea>';
document.getElementById("box2").innerHTML= '<textarea rows="4" onkeypress="CTRLC2(event)">Select some text and copy with CTRL-C.The <textarea> becomes hidden.</textarea>';
document.getElementById("testarea2").innerHTML= '<textarea rows="4" ">Paste text with CTRL-V</textarea>';
</script>
</body>
</html>
and the JS-file test.js
function CTRLC1(event) {
if (event.ctrlKey == true){
var x = event.which || event.keyCode;
if (x==99 || x==67){
setTimeout(function(){document.getElementById("box1").style.visibility ="hidden";}, 1);
}
}
}
function CTRLC2(event) {
if (event.ctrlKey == true){
var x = event.which || event.keyCode;
if (x==99 || x==67){
document.getElementById("box2").style.visibility ="hidden";
}
}
}
You should be using onkeydown instead of onkeypress.
In theory, the keydown and keyup events represent keys being pressed or released, while the keypress event represents a character being typed. The implementation of the theory is not same in all browsers. More info: onKeyPress Vs. onKeyUp and onKeyDown
Here is the fiddle: http://jsfiddle.net/lucianmoldovan/g5c0pgwj/3/
Notice that on the second box, though the box gets hidden the text doesn't get copied. I imagine that the element is being hidden before the text gets copied to clipboard. Adding that setTimeout fixes this, even if you set it at 0 milliseconds.

Replace text in textarea on his exact position [duplicate]

I'm trying to use Javascript to replace the selected text in an arbitrary selected TEXTAREA node in Chrome (! not a content editable div !) The code fragment I see repeated in lots of places to replace selected text basically does this:
var sel = window.getSelection();
var range = sel.getRangeAt(0);
range.insertNode( document.createTextNode("test "));
However, this does not work for input fields such as TEXTAREA or INPUT TYPE=TEXT. The text is inserted BEFORE the TEXTAREA instead of inside it.
There is an alternative method to modify the selection text inside a text area using textarea.selectionStart and textarea.selectionEnd. However, these require figuring out which textarea element is actually active/selected. Chrome/Webkit document.activeElement seems to be broken and has been broken for a long time. I can't figure out any workaround to find the "currently selected textarea". See the bug here...
http://code.google.com/p/chromium/issues/detail?id=14436
You can see a micro-demo of the problem I'm trying to solve here.
http://dj1.willowmail.com/~jeske/_drop/insertIssue/1.html
http://ajaxandxml.blogspot.com/2007/11/emulating-activeelement-property-with.html
Any thoughts on this?
Given a webpage with an arbitrary bit of text selected in an arbitrary TEXTAREA node, without knowing ahead of time what textarea the focus is in, how do I find the active textarea and replace the selected text with some other text?
(( FYI: I'm using this code in a Chrome extension. An in-page javascript content script is extending the page javascript, so I have no idea what the page structure is ahead of time. It needs to work for any webpage. ))
I think the problem you may be having is that the active element changes as a result of clicking the button before your code runs. If you instead use the mousedown event and prevent the default button action, it works fine in Chrome:
http://jsfiddle.net/b3Fk5/2/
It appears that as of 8/23/2012, Chrome does not properly support activeElement, as it is often set to "body" when it shouldn't be.
There may also be some challenges because in my chrome extension, right-clicking to get a context menu might be altering the activeElement.
The solution was to provide a focus handler to create a more reliable activeElement in Chrome, and then use direct interaction with the TEXTAREA to handle the selection replacement.
var dActiveElement = null;
function _dom_trackActiveElement(evt) {
if (evt && evt.target) {
dActiveElement = evt.target;
console.log("focus on: " + dActiveElement.nodeName +
" id: " + dActiveElement.id);
} else {
console.log("focus else..");
}
}
if (document.addEventListener) {
document.addEventListener("focus",_dom_trackActiveElement,true);
}
function insertTextAtCursor(text) {
console.log("insertTextAtCursor : " + text);
if (dActiveElement.nodeName.toUpperCase() == "TEXTAREA") {
console.log("selection in textarea! id: " + dActiveElement.id);
var ta = dActiveElement;
var saveSelectionStart = ta.selectionStart;
var newvalue = ta.value.slice(0,ta.selectionStart) +
text + ta.value.slice(ta.selectionEnd,ta.length);
console.log("output : " + newvalue + ", len : " + newvalue.length);
var newSelectionEnd = ta.selectionStart + text.length;
ta.value = newvalue;
ta.selectionStart = ta.selectionEnd = (newSelectionEnd);
}
}

Looking for ibooks html input alternative

In IOS5 on the iPad, iPad2 etc. iBooks accepted <input type="color"> as a way to prompt the keyboard to display when you clicked on an input field, to say, type in the answer to a question. I've just recently updated to IOS6, and this workaround no longer seems to be working. I tried using the JavaScript I found here - http://www.linkedin.com/groups/How-Show-iPads-Keyboard-when-3877009.S.84287009
<script type="text/javascript">
function iPadTouchHandler(event) {
var type = "",
button = 0; /*left*/
if (event.touches.length > 1)
return;
switch (event.type) {
case "touchstart":
// OLD: On iPad2 clicking on a text input field did not show the keyboard
// if ($(event.changedTouches[0].target).is("select")) {
// NEW: Now on iPad2 the touchstart-Event on input fields is ignored and everything works fine
// change my by Roland Caspers, Scheer Management
if ($(event.changedTouches[0].target).is("select") || $(event.changedTouches[0].target).is("input")) {
return;
}
iPadTouchStart(event); /*We need to trigger two events here to support one touch drag and drop*/
event.preventDefault();
return false;
break;
</script>
However this code seems to be outdated and relevant to IOS5. I know of a workaround, which is to put the page with the input into an iFrame, in that case you can just use <input type="text">, however I'd prefer to stay away from iFrames as they tend to move the content around depending on where the input box is. Any thoughts as to other possible solutions or workarounds? Tyvm :)
I am also Facing the same issue on iOS6 for , the same is working perfectly on the <iframe> tag. But it omits the images & style and etc.
Review the code "http://www.linkedin.com/groups/How-Show-iPads-Keyboard-when-3877009.S.84287009", I feel some thing has to modify on below condition:
($(event.changedTouches[0].target).is("select") || $(event.changedTouches[0].target).is("input"))
I'd be great if anyone provide the earlier response.
Thanks
I struggled with this same problem in iBooks on iOS 7. The tricky part was, that iBooks probably makes all text input fields disabled by default. We are using prototype.js, so here is my solution written for prototype:
$('my-input-field-id').observe('touchstart', function(event) {
var element = event.element();
if (element.disabled)
element.disabled = false;
element.focus();
event.stop();
});
So just listen for the 'touchstart' event on the input field and enable and focus the field when touched. It works for ordinary text fields (<input type="text">). Simple :-).

Multiple key presses in JavaScript only working in IE

I have the following code:
function handle_paste_keydown(key)
{
if(key.keyCode == 86 && key.ctrlKey) // Ctrl + V
{
alert("Test...");
}
}
This works in IE, but none of the other browsers. My reason for doing this is that I have finished creating a rich-text editor, but I need to handle the onpaste event carefully because formatted text is able to make it in to my editor, which could pose a minor risk to security, but also butchers my layout if malicious <span>s and <div>s make it in.
My current method is to give focus to an off-screen textarea, which means all code will be pasted in to that (which removes formatting); then I immediately grab the textarea.value and insert it at the current caret position in my contentEditable <div>.
So anyway, how do I get the Ctrl+V to work in all browsers and why doesn't it work in its current state?
Thank you.
If it works in IE but nowhere else you did something wrong.
Use the keypress event rather than keydown.
http://jsfiddle.net/Lxvgr/1/
document.getElementById('foo').onkeypress = function(e) {
if(e.charCode == 118 && e.ctrlKey) alert('pasted');
};
#Eric Sites: "use jQuery" isn't the answer to every javascript question. including an entire external framework to solve a simple 4byte issue like this is ridiculous.

How to change behavior of contenteditable blocks after on enter pressed in various browsers

When pressing enter in <div contenteditable="true"> </div> in firefox <br /> is produced - that's ok. But in Chrome or IE a new <div> or <p> is created. What should I do to make Chrome and IE behave like Firefox .
As Douglas said earlier, browsers try to clone previous tag when customer starts a new paragraph on an editable page. The discrepancy occurs when browser has nothing to depart from - e.g. when initially the page body is empty. In this case different browsers behave differently: IE starts to wrap every string into <p> tag, Chrome wraps each line in <div>.
To increase cross-browser experience, WebKit developers have introduced the DefaultParagraphSeparator command. You can use the following JavaScript on page loading for Chrome to change default paragraph separator to <p> tag:
document.execCommand('defaultParagraphSeparator', false, 'p');
The following will add a <br> when the enter key is pressed in all major browsers and attempts to place the caret directly after it. However, WebKit, Opera and IE all have issues with placing the caret correctly after the <br>, which the following code does not attempt to correct.
function enterKeyPressHandler(evt) {
var sel, range, br, addedBr = false;
evt = evt || window.event;
var charCode = evt.which || evt.keyCode;
if (charCode == 13) {
if (typeof window.getSelection != "undefined") {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
br = document.createElement("br");
range.insertNode(br);
range.setEndAfter(br);
range.setStartAfter(br);
sel.removeAllRanges();
sel.addRange(range);
addedBr = true;
}
} else if (typeof document.selection != "undefined") {
sel = document.selection;
if (sel.createRange) {
range = sel.createRange();
range.pasteHTML("<br>");
range.select();
addedBr = true;
}
}
// If successful, prevent the browser's default handling of the keypress
if (addedBr) {
if (typeof evt.preventDefault != "undefined") {
evt.preventDefault();
} else {
evt.returnValue = false;
}
}
}
}
var el = document.getElementById("your_editable_element");
if (typeof el.addEventListener != "undefined") {
el.addEventListener("keypress", enterKeyPressHandler, false);
} else if (typeof el.attachEvent != "undefined") {
el.attachEvent("onkeypress", enterKeyPressHandler);
}
Excellent references are located here on contenteditable.
http://blog.whatwg.org/the-road-to-html-5-contenteditable
Which leads to a really nice API here
http://dev.opera.com/articles/view/rich-html-editing-in-the-browser-part-1/
http://dev.opera.com/articles/view/rich-html-editing-in-the-browser-part-2/
If you're willing to take 30 minutes to an hour to read all this, you will absolutely not have to use some crappy third party editor like tinyMCE or ckeditor or whatever, you can build and customize it yourself and to be frank, it's easier AND faster to do it from scratch than to deal with all the cruft and unnecessary API of a third party WYSIWYG editor.
If you prefer to be happy rather than chase bugs :-) it would be much better to try to make FF use p or div as well. Not just besause it turned out to be a majority vote :-)
The reason is that br alone is borderline illegal if you look at a tag with XML eyes (it injects mixed data model - as in a text that's not guarded by a tag) and the thrend has been for years (by all browsers) towards full XML-ization.
Depending on your actual app it might be worth to try to put a div with fully formed style and definitely with some initial content - if you saw pages where you see grayed-out text like "type your comment here" and it dissapears the second you click into it (or remians - that's a design decision).
The reason for that part is that the semantics of "contenteditable" is "it already has content => browser has enough info to know what to do" so while browsers do their best to do something when faced with no content it makes the situation much more random.
I believe that if the div already has a paragraph tag inside it, and the user presses return while focus is inside that paragraph tag, then Firefox will insert another one. So, if you have this:
<div contenteditable="true">
<p> </p>
<p> </p>
</div>
and you focus, the press return, Firefox will insert a third paragraph.
You might be able to get around having the & nbsp ; in the paragraph tags by giving them minimum heights, and you might be able to get away with having only one. I never got to the bottom of the behaviour to my satisfaction the last time I looked at this. You'll probably need some JavaScript to enforce a minimum of one paragraph tag inside the div.

Categories