difference in mouse events on img/div - javascript

I have a little tool on a web page that can be clicked and dragged along a horizontal line. First I had the drag handle as an img. I had some problems with layout and realised later that it would probably be better to use a div with an img background.
So I changed the drag handle to use a div, and I discovered a pretty obvious error in my javascript code. I had the onMove and onUp events attached to the handle itself. So for example, if I clicked on the drag handle div and then moved my mouse upwards out of the div (as the div only moves left and right), it then doesn't catch the onUp or onMove events.
But the thing I don't understand, is why it worked perfectly when I was using an img tag. Is this a bug or is there something funny about images that makes them behave this way? Is it future-safe to leave use an img and leave the code as is?

When drag starts (onDown or
onClick), you should actually
dynamically attach onUp and onMove
events to document instead of
handle. Just don't forget to clear
them onUp.
However, there should be no difference
between <img> and <div> in this
case.
-- EDIT --
I stand corrected. Seems, there is slight difference in img and div handling in FF since FF3. It enables you to drag images to your computer, but we can get around it :).
Anyway, I have made some slight modifications to your script, cleaned it up a bit, and it should work now in all browsers. I have tested it on my computer with Firefox 3.5.5, Safari 4.0.3, Google Chrome, and Opera 10.1.
However, I don't have a way to test it on IE, so please report results.
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Page Title</title>
<script>
window.onload = function() { //After page is loaded this script will start automaticly
var divEl = document.getElementById("myDiv"); // Locate element with id "myDiv"
var imgEl = document.getElementById("myImg"); // Locate element with id "myImg"
divEl.onmousedown = onDown; // Assign funnction to event mousedown on "myDiv"
imgEl.onmousedown = onDown; // Assign funnction to event mousedown on "myImg"
function onDown(e) { // Mouse is down, long live the mouse :)
$el = this;
if (e.preventDefault) { e.preventDefault(e); } // This prevents default "drag image" behaivour
window.onmousemove = function(e) { // Assign function onmousemove to window
this.onmouseup = function(e) { // When we do a mouseup anywhere on window
window.onmousemove = undefined; // We clear onmouse from window
window.onmouseup = undefined; // We clear onmouseup from window
}
/* Part borowed from http://www.quirksmode.org/js/events_properties.html */
if (e.pageX) { posx = e.pageX; } // In case it is a normal browser
else if (e.clientX) { // In case it is IE
posx = e.clientX + document.body.scrollLeft+document.documentElement.scrollLeft;
}
/* End of borowed part */
$el.style.left = posx-parseInt($el.offsetWidth)/2+"px"; // Move this element to where mouse is
}
}
}
</script>
<style>
#myImg, #myDiv { display: block; position: relative; left: 0; width: 100px; height: 50px; }
#myImg { border: 1px solid red; }
#myDiv { border: 1px solid blue; background-color: lime;}
</style>
</head>
<body>
<img src="some_image.png" alt="my img" id="myImg">
<div id="myDiv">My div</div>
</body>
</html>
Ah, yes. CSS & JS in this example are in HTML only for testing purposes. In real code it is suggested you separate them to their own files.
Good luck :)

I'm still not sure what was causing the original behaviour in IE, but I've rewritten as Krule's suggestion and there is still a problem with Mozilla handling img tags differently to div tags. Here's an example, works fine in IE, doesn't work properly in Mozilla:
<script language="javascript" type="text/JavaScript">
var elem = null;
var downX = null;
var elemX = null;
function onDown(e) {
var ev = window.event ? window.event : e;
elem = window.event ? ev.srcElement : ev.target;
downX = ev.clientX;
elemX = parseInt(elem.style.left);
document.onmousemove = onMove;
document.onmouseup = onUp;
}
function onMove(e) {
var ev = window.event ? window.event : e;
elem.style.left = elemX + ev.clientX - downX + "px";
}
function onUp() {
document.onmousemove = null;
document.onmouseup = null;
}
</script>
<img src="" alt="my img" id="myImg" onmousedown="onDown(event);" style="position:relative; left:0px; width:100px; height:50px; border: solid 1px red;" />
<br />
<br />
<div onmousedown="onDown(event);" style="position:relative; left:0px; width:100px; height:50px; background-color:Lime; border: solid 1px blue;"><div>
Not sure if there is a way to override this behaviour, or if I'm going to have to rewrite everything yet again to use div instead of img.

Related

ondragstart - How to remove animation?

Whenever the user starts dragging an element with draggable="true", the element has a translucent copy of the element you are dragging. Here is the example from W3Schools:
stop starting animation
Right side of the photo is important. That is the animation you get when you start dragging an element.
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_ondrag
I have tried to use event.preventDefault(). However, the problem here is that this prevents onDrag from going at all while the element is moving and I need the data (mouse position and such) from onDrag.
It seems there are posts out there for how to stop the animation when you drop it but not start.
Just going off the example on the W3Schools site, I want the drag information without the ondragstart animation.
So if I modify the code by adding a preventDefault() on the ondragstart function:
function dragStart(event) {
console.log(event);
event.preventDefault(); //stops animation in right side of photo, but then won't let ondrag fire
event.dataTransfer.setData("Text", event.target.id);
}
function dragging(event) {
console.log(event);
document.getElementById("demo").innerHTML = "The p element is being dragged";
}
The animation you get when dragging goes away. However (as shown by my console.log) lines, the ondrag won't fire with this preventDefault on ondragstart. This is the information I need.
preventDefault inside ondrag doesn't stop that animation. Is this even possible?
SOLUTION
If anyone is wondering, I found what I needed to do in this case. You can set the image in javascript to a transparent one:
How to remove drag(Ghost) image?
so drag is still technically running but that ghostly image is gone. Wasn't thinking of right search terms.
In this case i would go for a different approach:
https://codepen.io/deibl31/pen/oNeXmPE?editors=1111
// HTML
<div id="myDiv">
</div>
// CSS
#myDiv {
position: absolute;
height: 100px;
width: 100px;
background: black;
color: white;
}
// JS
let myDiv = document.getElementById('myDiv');
let mouseDown = false;
let divPos = {x: 0, y: 0};
myDiv.addEventListener('mousedown', (event) => {
mouseDown = true;
});
window.addEventListener('mousemove', (event) => {
if (mouseDown) {
divPos.x = event.clientX;
divPos.y = event.clientY;
}
});
window.addEventListener('mouseup', (event) => {
if (mouseDown) {
myDiv.style.top = divPos.y + 'px';
myDiv.style.left = divPos.x + 'px';
}
mouseDown = false;
});
If anyone is wondering, I found what I needed to do in this case. You can set the image in JavaScript to a transparent one:
How to remove drag(Ghost) image?
so drag is still technically running but that ghostly image is gone. Wasn't thinking of right search terms.

Delay Gif until in viewport [duplicate]

I have a page with a lot of GIFs.
<img src="gif/1303552574110.1.gif" alt="" >
<img src="gif/1302919192204.gif" alt="" >
<img src="gif/1303642234740.gif" alt="" >
<img src="gif/1303822879528.gif" alt="" >
<img src="gif/1303825584512.gif" alt="" >
What I'm looking for
1 On page load => Animations for all gifs are stopped
2 On mouseover => Animations starts for that one gif
3 On mouseout => Animation stops again for that gif
I suppose this can be done in Jquery but I don't know how.
No, you can't control the animation of the images.
You would need two versions of each image, one that is animated, and one that's not. On hover you can easily change from one image to another.
Example:
$(function(){
$('img').each(function(e){
var src = $(e).attr('src');
$(e).hover(function(){
$(this).attr('src', src.replace('.gif', '_anim.gif'));
}, function(){
$(this).attr('src', src);
});
});
});
Update:
Time goes by, and possibilities change. As kritzikatzi pointed out, having two versions of the image is not the only option, you can apparently use a canvas element to create a copy of the first frame of the animation. Note that this doesn't work in all browsers, IE 8 for example doesn't support the canvas element.
I realise this answer is late, but I found a rather simple, elegant, and effective solution to this problem and felt it necessary to post it here.
However one thing I feel I need to make clear is that this doesn't start gif animation on mouseover, pause it on mouseout, and continue it when you mouseover it again. That, unfortunately, is impossible to do with gifs. (It is possible to do with a string of images displayed one after another to look like a gif, but taking apart every frame of your gifs and copying all those urls into a script would be time consuming)
What my solution does is make an image looks like it starts moving on mouseover. You make the first frame of your gif an image and put that image on the webpage then replace the image with the gif on mouseover and it looks like it starts moving. It resets on mouseout.
Just insert this script in the head section of your HTML:
$(document).ready(function()
{
$("#imgAnimate").hover(
function()
{
$(this).attr("src", "GIF URL HERE");
},
function()
{
$(this).attr("src", "STATIC IMAGE URL HERE");
});
});
And put this code in the img tag of the image you want to animate.
id="imgAnimate"
This will load the gif on mouseover, so it will seem like your image starts moving. (This is better than loading the gif onload because then the transition from static image to gif is choppy because the gif will start on a random frame)
for more than one image just recreate the script create a function:
<script type="text/javascript">
var staticGifSuffix = "-static.gif";
var gifSuffix = ".gif";
$(document).ready(function() {
$(".img-animate").each(function () {
$(this).hover(
function()
{
var originalSrc = $(this).attr("src");
$(this).attr("src", originalSrc.replace(staticGifSuffix, gifSuffix));
},
function()
{
var originalSrc = $(this).attr("src");
$(this).attr("src", originalSrc.replace(gifSuffix, staticGifSuffix));
}
);
});
});
</script>
</head>
<body>
<img class="img-animate" src="example-static.gif" >
<img class="img-animate" src="example-static.gif" >
<img class="img-animate" src="example-static.gif" >
<img class="img-animate" src="example-static.gif" >
<img class="img-animate" src="example-static.gif" >
</body>
That code block is a functioning web page (based on the information you have given me) that will display the static images and on hover, load and display the gif's. All you have to do is insert the url's for the static images.
I think the jQuery plugin freezeframe.js might come in handy for you. freezeframe.js is a jQuery Plugin To Automatically Pause GIFs And Restart Animating On Mouse Hover.
I guess you can easily adapt it to make it work on page load instead.
The best option is probably to have a still image which you replace the gif with when you want to stop it.
<img src="gif/1303552574110.1.gif" alt="" class="anim" >
<img src="gif/1302919192204.gif" alt="" class="anim" >
<img src="gif/1303642234740.gif" alt="" class="anim" >
<img src="gif/1303822879528.gif" alt="" class="anim" >
<img src="gif/1303825584512.gif" alt="" class="anim" >
$(window).load(function() {
$(".anim").src("stillimage.gif");
});
$(".anim").mouseover(function {
$(this).src("animatedimage.gif");
});
$(".anim").mouseout(function {
$(this).src("stillimage.gif");
});
You probably want to have two arrays containing paths to the still and animated gifs which you can assign to each image.
found a working solution here:
https://codepen.io/hoanghals/pen/dZrWLZ
JS here:
var gifElements = document.querySelectorAll('img.gif');
for(var e in gifElements) {
var element = gifElements[e];
if(element.nodeName == 'IMG') {
var supergif = new SuperGif({
gif: element,
progressbar_height: 0,
auto_play: false,
});
var controlElement = document.createElement("div");
controlElement.className = "gifcontrol loading g"+e;
supergif.load((function(controlElement) {
controlElement.className = "gifcontrol paused";
var playing = false;
controlElement.addEventListener("click", function(){
if(playing) {
this.pause();
playing = false;
controlElement.className = "gifcontrol paused";
} else {
this.play();
playing = true;
controlElement.className = "gifcontrol playing";
}
}.bind(this, controlElement));
}.bind(supergif))(controlElement));
var canvas = supergif.get_canvas();
controlElement.style.width = canvas.width+"px";
controlElement.style.height = canvas.height+"px";
controlElement.style.left = canvas.offsetLeft+"px";
var containerElement = canvas.parentNode;
containerElement.appendChild(controlElement);
}
}
Pure JS implementation https://jsfiddle.net/clayrabbit/k2ow48cy/
(based on canvas solution from https://codepen.io/hoanghals/pen/dZrWLZ)
[].forEach.call(document.querySelectorAll('.myimg'), function(elem) {
var img = new Image();
img.onload = function(event) {
elem.previousElementSibling.getContext('2d').drawImage(img, 0, 0);
};
img.src = elem.getAttribute('data-src');
elem.onmouseover = function(event) {
event.target.src = event.target.getAttribute('data-src');
};
elem.onmouseout = function(event) {
event.target.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=";
};
});
.mydiv{
width: 320px;
height: 240px;
position: relative;
}
.mycanvas, .myimg {
width: 100%;
height: 100%;
position: absolute;
}
<div class="mydiv">
<canvas class="mycanvas" width=320 height=240></canvas>
<img class="myimg" data-src="https://media.giphy.com/media/Byana3FscAMGQ/giphy.gif">
</div>
You can solve this by having a long stripe that you show in steps, like a filmstrip. Then you can stop the film on any frame.
Example below (fiddle available at http://jsfiddle.net/HPXq4/9/):
the markup:
<div class="thumbnail-wrapper">
<img src="blah.jpg">
</div>
the css:
.thumbnail-wrapper{
width:190px;
height:100px;
overflow:hidden;
position:absolute;
}
.thumbnail-wrapper img{
position:relative;
top:0;
}
the js:
var gifTimer;
var currentGifId=null;
var step = 100; //height of a thumbnail
$('.thumbnail-wrapper img').hover(
function(){
currentGifId = $(this)
gifTimer = setInterval(playGif,500);
},
function(){
clearInterval(gifTimer);
currentGifId=null;
}
);
var playGif = function(){
var top = parseInt(currentGifId.css('top'))-step;
var max = currentGifId.height();
console.log(top,max)
if(max+top<=0){
console.log('reset')
top=0;
}
currentGifId.css('top',top);
}
obviously, this can be optimized much further, but I simplified this example for readability
A more elegant version of Mark Kramer's would be to do the following:
function animateImg(id, gifSrc){
var $el = $(id),
staticSrc = $el.attr('src');
$el.hover(
function(){
$(this).attr("src", gifSrc);
},
function(){
$(this).attr("src", staticSrc);
});
}
$(document).ready(function(){
animateImg('#id1', 'gif/gif1.gif');
animateImg('#id2', 'gif/gif2.gif');
});
Or even better would be to use data attributes:
$(document).ready(function(){
$('.animated-img').each(function(){
var $el = $(this),
staticSrc = $el.attr('src'),
gifSrc = $el.data('gifSrc');
$el.hover(
function(){
$(this).attr("src", gifSrc);
},
function(){
$(this).attr("src", staticSrc);
});
});
});
And the img el would look something like:
<img class="animated-img" src=".../img.jpg" data-gif-src=".../gif.gif" />
Note: This code is untested but should work fine.
For restarting the animation of a gif image, you can use the code:
$('#img_gif').attr('src','file.gif?' + Math.random());
Related answer, you can specify the number of playbacks on a gif. The below gif has 3 playbacks associated with it (10 second timer, 30 second playback total). After 30 seconds have passed since page load, it stops at "0:01".
Refresh the page to restart all 3 playbacks
You have to modify the gif itself. An easy tool is found here for modifying GIF playbacks https://ezgif.com/loop-count.
To see an example of a single-loop playback gif in action on a landing page, checkout this site using a single playback gif https://git-lfs.github.com/
This answer builds on that of Sourabh, who pointed out an HTML/CSS/JavaScript combo at https://codepen.io/hoanghals/pen/dZrWLZ that did the job. I tried this, and made a complete web page including the CSS and JavaScript, which I tried on my site. As CodePens have a habit of disappearing, I decided to show it here. I'm also showing a simplified stripped-to-essentials version, to demonstrate the minimum that one needs to do.
I must also note one thing. The code at the above link, whose JavaScript Sourabh copies, refers to a JavaScript constructor SuperGif() . I don't think Sourabh explained that, and neither does the CodePen. An easy search showed that it's defined in buzzfeed /
libgif-js , which can be downloaded from https://github.com/buzzfeed/libgif-js#readme . Look for the control that the red arrow below is pointing at, then click on the green "Code" button. (N.B. You won't see the red arrow: that's me showing you where to look.)
A menu will pop up offering various options including to download a zip file. Download it, and extract it into your HTML directory or a subdirectory thereof.
Next, I'm going to show the two pages that I made. The first is derived from the CodePen. The second is stripped to its essentials, and shows the minimum you need in order to use SuperGif.
So here's the complete HTML, CSS, and JavaScript for the first page. In the head of the HTML is a link to libgif.js , which is the file you need from the zip file. Then, the body of the HTML starts with some text about cat pictures, and follows it with a link to an animated cat GIF at https://media.giphy.com/media/Byana3FscAMGQ/giphy.gif .
It then continues with some CSS. The CodePen uses SCSS, which for anyone who doesn't know, has to be preprocessed into CSS. I've done that, so what's in the code below is genuine CSS.
Finally, there's the JavaScript.
<html>
<head>
<script src="libgif-js-master/libgif.js"></script>
</head>
<body>
<div style="width: 600px; margin: auto; text-align: center; font-family: arial">
<p>
And so, the unwritten law of the internet, that any
experiment involving video/images must involve cats in
one way or another, reared its head again. When would
the internet's fascination with cats come to an end?
Never. The answer is "Never".
</p>
<img src='https://media.giphy.com/media/Byana3FscAMGQ/giphy.gif' class='gif' />
</div>
<style>
img.gif {
visibility: hidden;
}
.jsgif {
position: relative;
}
.gifcontrol {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
cursor: pointer;
transition: background 0.25s ease-in-out;
z-index: 100;
}
.gifcontrol:after {
transition: background 0.25s ease-in-out;
position: absolute;
content: "";
display: block;
left: calc(50% - 25px);
top: calc(50% - 25px);
}
.gifcontrol.loading {
background: rgba(255, 255, 255, 0.75);
}
.gifcontrol.loading:after {
background: #FF9900;
width: 50px;
height: 50px;
border-radius: 50px;
}
.gifcontrol.playing {
/* Only show the 'stop' button on hover */
}
.gifcontrol.playing:after {
opacity: 0;
transition: opacity 0.25s ease-in-out;
border-left: 20px solid #FF9900;
border-right: 20px solid #FF9900;
width: 50px;
height: 50px;
box-sizing: border-box;
}
.gifcontrol.playing:hover:after {
opacity: 1;
}
.gifcontrol.paused {
background: rgba(255, 255, 255, 0.5);
}
.gifcontrol.paused:after {
width: 0;
height: 0;
border-style: solid;
border-width: 25px 0 25px 50px;
border-color: transparent transparent transparent #ff9900;
}
</style>
<script>
var gifElements = document.querySelectorAll('img.gif');
for(var e in gifElements) {
var element = gifElements[e];
if(element.nodeName == 'IMG') {
var supergif = new SuperGif({
gif: element,
progressbar_height: 0,
auto_play: false,
});
var controlElement = document.createElement("div");
controlElement.className = "gifcontrol loading g"+e;
supergif.load((function(controlElement) {
controlElement.className = "gifcontrol paused";
var playing = false;
controlElement.addEventListener("click", function(){
if(playing) {
this.pause();
playing = false;
controlElement.className = "gifcontrol paused";
} else {
this.play();
playing = true;
controlElement.className = "gifcontrol playing";
}
}.bind(this, controlElement));
}.bind(supergif))(controlElement));
var canvas = supergif.get_canvas();
controlElement.style.width = canvas.width+"px";
controlElement.style.height = canvas.height+"px";
controlElement.style.left = canvas.offsetLeft+"px";
var containerElement = canvas.parentNode;
containerElement.appendChild(controlElement);
}
}
</script>
</body>
</html>
When I put the page on my website and displayed it, the top looked like this:
And when I pressed the pink button, the page changed to this, and the GIF started animating. (The cat laps water falling from a tap.)
To end, here's the second, simple, page. Unlike the first, this doesn't have a fancy Play/Pause control that changes shape: it just has two buttons. The only thing the code does that isn't essential is to disable whichever button is not relevant, and to insert some space between the buttons.
<html>
<head>
<script src="libgif-js-master/libgif.js"></script>
</head>
<body>
<button type="button" onclick="play()"
id="play_button"
style="margin-right:9px;"
>
Play
</button>
<button type="button" onclick="pause()"
id="pause_button"
>
Pause
</button>
<img src="https://media.giphy.com/media/Byana3FscAMGQ/giphy.gif"
id="gif"
/>
<script>
var gif_element = document.getElementById( "gif" );
var supergif = new SuperGif( {
gif: gif_element,
progressbar_height: 0,
auto_play: false
} );
supergif.load();
function play()
{
var play_button = document.getElementById( "play_button" );
play_button.disabled = true;
var pause_button = document.getElementById( "pause_button" );
pause_button.disabled = false;
supergif.play();
}
function pause()
{
var play_button = document.getElementById( "play_button" );
play_button.disabled = false;
var pause_button = document.getElementById( "pause_button" );
pause_button.disabled = true;
supergif.pause();
}
pause_button.disabled = true;
</script>
</body>
</html>
This, plus the example.html file in libgif-js, should be enough to get anyone started.
There is only one way from what I am aware.
Have 2 images, first a jpeg with first frame(or whatever you want) of the gif and the actual gif.
Load the page with the jpeg in place and on mouse over replace the jpeg with the gif. You can preload the gifs if you want or if they are of big size show a loading while the gif is loading and then replace the jpeg with it.
If you whant it to bi linear as in have the gif play on mouse over, stop it on mouse out and then resume play from the frame you stopped, then this cannot be done with javascript+gif combo.
Adding a suffix like this:
$('#img_gif').attr('src','file.gif?' + Math.random());
the browser is compelled to download a new image every time the user accesses the page. Moreover the client cache may be quickly filled.
Here follows the alternative solution I tested on Chrome 49 and Firefox 45.
In the css stylesheet set the display property as 'none', like this:
#img_gif{
display:'none';
}
Outside the '$(document).ready' statement insert:
$(window).load(function(){ $('#img_gif').show(); });
Every time the user accesses the page, the animation will be started after the complete load of all the elements. This is the only way I found to sincronize gif and html5 animations.
Please note that:
The gif animation will not restart after refreshing the page (like pressing "F5").
The "$(document).ready" statement doesn't produce the same effect of "$(window).load".
The property "visibility" doesn't produce the same effect of "display".
css filter can stop gif from playing in chrome
just add
filter: blur(0.001px);
to your img tag then gif freezed to load via chrome performance concern :)

Prevent user from copying text on mobile browsers

I'm trying to develop a typing speed competition using JavaScript. People should write all the words they see from a div to a textarea.
To prevent cheating (like copying the words from div) one way is check the written words only when a keyboard key is down, but I was wondering if there is a way to prevent the user from copying the text in a browser?
What I have tried so far:
Disable right click (didn't work on mobile browsers)
Show an alert using the onmousedown event in all the page (it didn't work either)
Using any libraries is OK.
You can simply make the text into an image.
<style type="text/css">
div.image {
width: 100px;
height: 100px;
background-image: url-to-your-image;
}
</style>
To generate the images you can use a server side script as in the aswers of this question
or something like this:
<?php
header("Content-type: image/png");
$im = #imagecreate(210, 30)
or die("Cannot Initialize new GD image stream");
$background_color = imagecolorallocate($im, 255, 255, 255);
$text_color = imagecolorallocate($im, 0, 0, 0);
imagestring($im, 4, 5, 5, "This is a test", $text_color);
imagepng($im);
imagedestroy($im);
?>
Test here
You can prevent the user from actually selecting the text so it can not be copied - however I'd still combine this with paste detection as others recommended
.noselect {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
<p>this can be selected</p>
<p class="noselect">this can NOT be selected</p>
But the user can still open the page source and copy it from there.
One crazy way of doing this is, laying out another absolutely positioned element on top of this. But this will disallow clicking of links too! May be you can do it with position: relative and a higher z-index.
.content {position: relative;}
.content .mask {position: absolute; z-index: 1; width: 100%; height: 100%;}
.content a {position: relative; z-index: 3;}
<div class="content">
<div class="mask"></div>
<p>Pages that you view in incognito tabs won’t stick around in your browser’s history, cookie store or search history after you’ve closed <strong>all</strong> of your incognito tabs. Any files that you download or bookmarks that you create will be kept. Learn more about incognito browsing</p>
</div>
Try using the touch or longpress events.
<!DOCTYPE html>
<html>
<head>
<script>
function absorbEvent_(event) {
var e = event || window.event;
e.preventDefault && e.preventDefault();
e.stopPropagation && e.stopPropagation();
e.cancelBubble = true;
e.returnValue = false;
return false;
}
function preventLongPressMenu(node) {
node.ontouchstart = absorbEvent_;
node.ontouchmove = absorbEvent_;
node.ontouchend = absorbEvent_;
node.ontouchcancel = absorbEvent_;
}
function init() {
preventLongPressMenu(document.getElementById('theimage'));
}
</script>
</head>
<body onload="init()">
<img id="theimage" src="http://www.google.com/logos/arthurboyd2010-hp.jpg" width="400">
</body>
</html>
Source
Try putting a transparent div over the text.
I have used jQuery here.
That should work.
var position = $('#textInHere').position();
$('#noClickThroughThis').css({
height: ($('#textInHere').height()),
width: ($('#textInHere').width()),
position: 'absolute',
top: position.top,
left: position.left,
'z-index': 100
});
Here is a fiddle
http://jsfiddle.net/lacrioque/tc4bwejn/
It's easy to disable the paste feature by using jQuery. For example, if you have an edit field like this one:
<p id='someInput' contenteditable='true'>Here is the text</p>
Then, this piece of jQuery code will disable the pasting feature on it:
$('#someInput').on('paste', function(e) {
return false;
});
A good way to work out if a user is cheating is to compare the current input length to the last input length. You can use a data attribute to store the previous value (or length):
<textarea class="typing-only" data-temp=""></textarea>
jQuery:
$(document).on('input', '.typing-only', function(){
if((this.value.length - 1) > $(this).data('temp').length){
alert('Cheat!');
}
$(this).data('temp', this.value);
});
JSFiddle demo
pointer-events: none
CSS pointer-events allows you to control the interaction between an element and the mouse. When set to none, the element is never the target of mouse events.
MDN definition page
You can try using :after tag and styling it with content: "Text"; in css, AFAIK you cannot select :before and :after's content.
Thanks for your amazing solutions. I tested all of them, and in short some of them worked only on a PC, some only on Chrome and Firefox and some only on Safari, but unfortunately none of them worked 100%.
Although #Max answer might be safest, I didn't tag with PHP in the question because if I use this solution dealing with answers, it will be hard because I don't have access to words on the client side!
So the ultimate solution I came with was combining all of the provided answers plus some new methods (like clearing the clipboard every second) into a jQuery plugin. Now it works on multiple elements too and worked 100% on PC browsers, Firefox, Chrome, and Safari.
What this plugin does
Prevent pasting (optional)
Clearing clipboard (looks like it doesn't work well)
Absorbs all touch events
Disable right click
Disable user selections
Disable pointer events
Add a mask with a z-index inside any selected DOM
Add a transparent div on any selected DOM
A jsFiddle:
(function($) {
$.fn.blockCopy = function(options) {
var settings = $.extend({
blockPasteClass : null
}, options);
if(settings.blockPasteClass){
$("." + settings.blockPasteClass ).bind('copy paste cut drag drop', function (e) {
e.preventDefault();
return false;
});
}
function style_appender(rule){
$('html > head').append($('<style>'+rule+'</style>'));
}
function html_appender(html){
$("body").append(html);
}
function clearClipboard() {
var $temp = $("#bypasser");
$temp.val("You can't cheat !").select();
document.execCommand("copy");
}
function add_absolute_div(id) {
html_appender("<div id='noClick"+id+"' onclick='return false;' oncontextmenu='return false;'> </div>");
}
function absorbEvent_(event) {
var e = event || window.event;
e.preventDefault && e.preventDefault();
e.stopPropagation && e.stopPropagation();
e.cancelBubble = true;
e.returnValue = false;
return false;
}
function preventLongPressMenu(node) {
node.ontouchstart = absorbEvent_;
node.ontouchmove = absorbEvent_;
node.ontouchend = absorbEvent_;
node.ontouchcancel = absorbEvent_;
}
function set_absolute_div(element,id){
var position = element.position();
var noclick = "#noClick" + id;
$(noclick).css({
height: (element.height()),
width: (element.width()),
position: 'absolute',
top: position.top,
left: position.left,
'z-index': 100
})
}
$("body").bind("contextmenu", function(e) {
e.preventDefault();
});
//Append needed rules to CSS
style_appender(
"* {-moz-user-select: none !important; -khtml-user-select: none !important; -webkit-user-select: none !important; -ms-user-select: none !important; user-select: none !important; }"+
".content {position: relative !important; }" +
".content .mask {position: absolute !important ; z-index: 1 !important; width: 100% !important; height: 100%!important;}" +
".content a {position: relative !important; z-index: 3 !important;}"+
".content, .content .mask{ pointer-events: none;}"
);
//Append an input to clear the clipboard
html_appender("<input id='bypasser' value='nothing' type='hidden'>");
//Clearing clipboard Intervali
setInterval(clearClipboard,1000);
var id = 1;
return this.each( function() {
//Preventing using touch events
preventLongPressMenu($(this));
//Add CSS preventer rules to selected DOM & append mask to class
$(this).addClass("content").append("<div class='mask'></div>");
//Append an absolute div to body
add_absolute_div(id);
//Set position of the div to selected DOM
set_absolute_div($(this),id);
id++;
});
}
}(jQuery));
Usage
$(document).ready(function(){
$(".words").blockCopy({
blockPasteClass : "noPasting"
});
});
HTML for demo:
<div class="words">Test1: Can you copy me or not?</div><br>
<div class="words">Test2: Can you <br> copy me or not?</div><br>
<textarea class="words">Test3: Can you <br>copy me or not?</textarea><br>
<textarea class="noPasting" placeholder="Test1: Paste content if you can" ></textarea><br>
<textarea class="noPasting" placeholder="Test2: Paste content if you can" ></textarea>
Let me know your opinions. Thanks.
Sources
Answers to this question
Copy text to clipboard
Add CSS rule using jQuery
A simpler solution than the accepted one would be to simply use a canvas element with filltext
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillText("Can't copy this", 5, 30);
<canvas id="myCanvas"></canvas>
JSFiddle example
You can return false on jQuery's cut copy paste events.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).on("cut copy paste", function(event){
return false;
});
</script>
<textarea>Try to copy my text</textarea>

Is it possible to detect when the mouse is over the browser tab?

I was assigned a task to make a script which will generate a pop-up window when the mouse cursor goes over the current opened browser tab.
Something like this demo : http://demo.exitmonitor.com/ but here the pop-up appears always when the mouse leaves the top part of the web page.
I need to generate this window exactly when the mouse is over my current active browser tab.
Is it possible to do this with javascript?
I believe you need mouseleave event:
<script>
function addEvent(obj, evt, fn) {
if (obj.addEventListener) {
obj.addEventListener(evt, fn, false);
}
else if (obj.attachEvent) {
obj.attachEvent("on" + evt, fn);
}
}
addEvent(window, "load", function (e) {
addEvent(document, "mouseleave", function (e) {
e = e ? e : window.event;
var from = e.relatedTarget || e.toElement;
if (!from || from.nodeName == "HTML") {
//.... do_this
}
});
});
</script>
I assume by "tab" you mean the area highlighted in red:
In all modern browsers a website cannot access anything out of its window, except for APIs explicitly provided to it.
Therefore, there is not way for you to even access the tab bar with just JavaScript.
Whether or not there is at all a way to get access to the tab bar depends on the browser, but it will (most certainly) require a browser addon.
In Chrome, for example, this was not at all possible back in 2010 and it looks like nothing has changed there.
In Firefox however, an addon can actually do this.
Assuming you know how to attach a script to browser.xul, I'm leaving out install.rdf, chrome.manifest and overlay.xul, so here's only the relevant JavaScript:
(function()
{
// Wait for the browser to settle
top.addEventListener('load', function load(event)
{
// It only needs to do that once
top.removeEventListener('load', load);
// Get notified about every page that loads
top.gBrowser.addEventListener('DOMContentLoaded', function(event)
{
// Get the current tab
var tab = top.gBrowser.mCurrentTab;
// Check if we already annoyified it
if(tab.annoyingOrange === undefined)
{
// If not, let's do that!
tab.annoyingOrange = 'Plumpkin';
// Add a mouseover event to it
top.gBrowser.mCurrentTab.addEventListener('mouseover', function(ev)
{
// Since we do that to all tabs, we need to check here if we're still the selected tab
if(ev.target == top.gBrowser.mCurrentTab)
{
// And now we can get onto everybody's nerves!
alert('Hey apple!!!');
}
});
}
});
});
})();
Tested with Firefox 37.0.1 on Windows.
[ Download .xpi ] (Protip: Unzip for source)
But if your browser does not support it, you are out of luck and there is nothing you can do!
Anyway, this is a very bad thing to do and it annoys people to no end!
This should never, never ever be done in a production or even beta environment!
This works with modern jquery versions 1.8.1 +
Popup will show when user clicks above the current window as if they were going to switch tabs or perform some other action. It only pops once so it's less intrusive and it's all customizable message wise and style.
html
<div id='confirm'>
<div class='message'></div>
<button class='yes'>OK</button>
</div>
css
#confirm {display:none; top:50%; left:50%; transform:translate(-50%, -50%); background:#595959; position:fixed; width:650px; height:auto; padding:40px; text-align:center; box-shadow:0px 0px 22px -2px rgba(0,0,0,0.5); z-index:1000;}
#confirm button {width:100%; height:70px; color:#fff; background:#595959; display:inline-block; border:1px solid #fff; text-align:center; font-size:12pt; letter-spacing:2px; margin-top:40px; bottom:10px; cursor:pointer; opacity:0.5; transition:0.9s;}
#confirm button:hover {opacity:1;}
#confirm .message {color:#fff;}
js
var mouseX = 0;
var mouseY = 0;
var popupCounter = 0;
document.addEventListener("mousemove", function(e) {
mouseX = e.clientX;
mouseY = e.clientY;
document.getElementById("coordinates").innerHTML = "<br />X: " + e.clientX + "px<br />Y: " + e.clientY + "px";
});
$(document).mouseleave(function (msg, myYes) {
if (mouseY < 100) {
if (popupCounter < 1) {
msg = "There's something in your basket, are you sure you want to leave?";
var confirmBox = $("#confirm");
confirmBox.find(".message").text(msg);
confirmBox.find(".yes").unbind().click(function() {
confirmBox.hide();
});
confirmBox.find(".yes").click(myYes);
confirmBox.show();
}
popupCounter ++;
}
});
https://jsfiddle.net/pgx7c5d4/
Use MouseEvent.clientX and MouseEvent.clientY to track where the mouse is on the document, and then put the popup there using absolute positioning:
//The popup:
var span = document.createElement("span");
span.style.position = "absolute";
span.textContent = "I'm a popup!";
//When the page loads, the popup will be in the top-left corner
//because we can't know where the mouse is on the page load.
document.body.insertBefore(span, document.body.firstChild);
//The event:
document.addEventListener("mousemove", function(e) {
//Position the span according to its dimensions and where the mouse is.
span.style.marginLeft = e.clientX-span.clientWidth/2+"px";
span.style.marginTop = e.clientY-span.clientHeight/2+"px";
});

How to capture mouseup event outside of mousedown element? (i.e. for proper Drag-n-Drop?)

The Javascript onmouseup event is not triggered if the mouse button is released outside the element on which onmousedown has been triggered.
This causes a drag&drop bug in JQuery UI: A JQuery draggable element does not stop dragging when mouse button is released outside of its container (because the element will stop moving when reaching it's parent boundaries). Steps to reproduce:
Go to http://jqueryui.com/draggable/.
Drag the draggable downward until the mouse has left the surrounding container
Release mouse button (no mouse button is pressed at this point)
Move mouse back into container
And the draggable is still being dragged. I would expect the dragging to have stopped as soon as I released the mouse button - no matter where it is released.
I see that behavior in latest Chrome and IE.
Is there any work-around?
I know that we could stop dragging the container on mouseout or mouseleave, but I would like to keep dragging, even if I am outside the parent container, much like in google maps (no matter, where you release the mouse, it always stops dragging the map).
You can have your mousedown element "capture" the pointer. Then it would always receive the mouseup event. In React this could look like this:
const onPointerDownDiv1 = (event: React.PointerEvent) => {
(event.target as HTMLDivElement).setPointerCapture(event.pointerId);
// Do something ...
};
const onPointerUpDiv1 = (event: React.PointerEvent) => {
(event.target as HTMLDivElement).releasePointerCapture(event.pointerId);
// Do something ...
};
<div
ref={div1}
id="div1"
className="absolute top-[200px] left-[390px] h-8 w-8 bg-red-300"
onPointerDown={onPointerDownDiv1}
onPointerUp={onPointerUpDiv1}
/>
And here is an implementation using "plain vanilla" html + javascript:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div
id="div1"
style="
position: absolute;
left: 50px;
top: 50px;
width: 20px;
height: 20px;
background-color: red;
"
></div>
</body>
<script>
let isDragging = false;
let offsetX = 0;
let offsetY = 0;
let divElement = document.getElementById("div1");
divElement.addEventListener("pointerdown", onPointerDown);
divElement.addEventListener("pointermove", onPointerMove);
divElement.addEventListener("pointerup", onPointerUp);
function onPointerDown(event) {
divElement.setPointerCapture(event.pointerId);
offsetX = event.clientX - divElement.offsetLeft;
offsetY = event.clientY - divElement.offsetTop;
isDragging = true;
}
function onPointerMove(event) {
if (isDragging) {
divElement.style.left = (event.clientX - offsetX).toString() + "px";
divElement.style.top = (event.clientY - offsetY).toString() + "px";
}
}
function onPointerUp(event) {
divElement.releasePointerCapture(event.pointerId);
isDragging = false;
}
</script>
</html>
I found this to be the best solution: Attach the mouseup event handler to document instead. Then it will always cancel, even if you release the mouse button outside the browser. Of course, this is not a pretty solution, but apparently, this is the only way to get dragging to work correctly.
Try the solution below:
You will see that "Drag END" will always happen, no matter where you release the cursor.
Also, in order to prevent text selection while dragging, I added an unselectable class.
let dragging = false;
const dragEl = document.querySelector('div');
const logEl = document.querySelector('pre');
dragEl.addEventListener('mousedown touchstart', (evt) => {
dragging = true;
dragEl.classList.add('unselectable');
logEl.textContent += 'drag START\n';
});
document.addEventListener('mouseup touchend', (evt) => {
if (dragging) {
event.preventDefault();
dragEl.classList.remove('unselectable');
dragging = false;
logEl.textContent += 'drag END\n';
}
});
div {
background: red;
}
.unselectable {
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard */
}
<div>drag me</div>
<hr>
LOG:
<p><pre></pre></p>
Update
These days, the setPointerCapture API provides a cleaner solution, as explained in this answer.
The best way to handle this is to check the current button status in the mousemove event handler. If the button is no longer down, stop dragging.
If you put the mouseup event on the document then you just move the problem up, the original problem will still show itself if you release the button outside of the document (or even outside of the browser window).
For example with jQuery
$div.on("mousedown", function (evt) {
dragging = true;
}).on("mouseup", function (evt) {
dragging = false;
}).on("mousemove", function (evt) {
if (dragging && evt.button == 0) {
//Catch case where button released outside of div
dragging = false;
}
if (dragging) {
//handle dragging here
}
});
You could skip the mouseup event handler because it will be caught by the mousemove but I think it's more readable with it still there.
If you create an "index.html" file with the following code in it (you can just copy and paste it entirely) it will show the working solution using just plain vanilla html + javascript.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div
id="div1"
style="
position: absolute;
left: 50px;
top: 50px;
width: 20px;
height: 20px;
background-color: red;
"
></div>
</body>
<script>
let isDragging = false;
let offsetX = 0;
let offsetY = 0;
let divElement = document.getElementById("div1");
divElement.addEventListener("pointerdown", onPointerDown);
divElement.addEventListener("pointermove", onPointerMove);
divElement.addEventListener("pointerup", onPointerUp);
function onPointerDown(event) {
divElement.setPointerCapture(event.pointerId);
offsetX = event.clientX - divElement.offsetLeft;
offsetY = event.clientY - divElement.offsetTop;
isDragging = true;
}
function onPointerMove(event) {
if (isDragging) {
let newPosLeft = event.clientX - offsetX;
if (newPosLeft < 30) {
newPosLeft = 30;
}
let newPosTop = event.clientY - offsetY;
if (newPosTop < 30) {
newPosTop = 30;
}
divElement.style.left = newPosLeft.toString() + "px";
divElement.style.top = newPosTop.toString() + "px";
}
}
function onPointerUp(event) {
divElement.releasePointerCapture(event.pointerId);
isDragging = false;
}
</script>
</html>

Categories

Resources