jQuery update (1.7) breaks event coords on touch event? - javascript

This jsfiddle reproduces the problem and allows you to quickly switch between 1.6.4 and 1.7.1.
You'll see that LIs stop being detected, because the coords are undefined after switching to 1.7.1
http://jsfiddle.net/eHHgP/2/
How can I fix this problem without downgrading?
body{
font-family: sans-serif;
}
#other{
position: absolute;
height: 300px;
width: 300px;
background: rgba(0,0,0,0.5);
}
#other ul{
position: absolute;
top: 50px;
right: 50px;
bottom: 50px;
left: 50px;
width: 200px;
height: 200px;
}
#other li{
display: block;
margin: 25px;
height: 50px;
width: 50px;
color: white;
background: rgba(0,0,0,0.5);
float: left;
<div id="other">
<ul>
<li>LI</li>
<li>LI</li>
<li>LI</li>
<li>LI</li>
</ul>
</div>
<div id="output">
output
<div>
$(document).bind('touchmove', function(event){
event.preventDefault();
});
$('#other').bind('touchstart touchmove', function(event){
element = document.elementFromPoint(event.pageX, event.pageY);
console.log(event);
if(element.nodeName === 'LI'){
$('#output').html('LI');
}else{
$('#output').html('NOT LI');
}
});

It turns out that in 1.7, only events which pass this regexp have certain "mouse properties" (like .pageX) passed through to the jQuery event object:
/^(?:mouse|contextmenu)|click/
Obviously, touchstart etc. don't pass this regexp. So you'd have to mark these events as being mouse events yourself, as jQuery does here. You can do it this way if you want to go for conciseness:
// add more if necessary, I don't know much about touch events
$.each("touchstart touchmove touchend".split(" "), function(i, name) {
jQuery.event.fixHooks[name] = jQuery.event.mouseHooks;
});

Also another way to handle this is using the proper e.changedTouches. So it would work like this:
if (typeof e.changedTouches !== 'undefined') {
pageX = e.changedTouches[0].pageX;
pageY = e.changedTouches[0].pageY;
} else {
pageX = e.pageX;
pageY = e.pageY;
}
Hope this helps.

Related

Avoid mousemove on absolutely positioned children elements

I am trying to understand mousemove Event on absolute elements.
I made a Codepen to demonstrate what i wanted.
I need mousemove to be captured on #main element, But not on any of its children that are positioned absolutely.
HTML:
<div id="main">
<div class="btn">Click Me</div>
</div>
<div id="output">
</div>
JS:
$(document).ready(function(){
$('#main').on('mousemove',function( e ) {
var msg = "mouse move ";
msg += e.pageX + ", " + e.pageY;
$( "#output" ).html(msg);
});
});
Simply check who's the target in your event handler, and if it is one of those you want to block the event, return early.
In current configuration, it is the same as checking if the target is indeed #main, a.k.a jQuery's $event.currentTarget.
$('#main').on('mousemove', function(e) {
// here we only want the events of #main
// so any other target is irrelevant
if (e.target !== e.currentTarget) return;
var msg = "mouse move ";
msg += e.pageX + ", " + e.pageY;
$("#output").html(msg);
});
$('#main').on('mouseout', function() {
$("#output").html('');
});
$('#main>.btn').on('click', e=> $("#output").html('can still click'));
#main {
float: left;
background: yellow;
width: 400px;
height: 400px;
position: relative;
}
#main .btn {
position: absolute;
top: 20px;
left: 20px;
z-index: 2;
border: 0;
background: blue;
color: #FFF;
}
#main .btn .innerbtn {
padding: 10px;
}
#output {
display: inline-block;
background: #efefef;
width: 200px;
position: absolute;
right: 0;
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="main">
<div class="btn">
<div class="innerbtn">Click Me</div>
</div>
</div>
<div id="output">
</div>

Show element if another element is outside screen (JS)

If element1 is outside visible area, element2 should be visible. If element1 is inside visible area, element2 should be hidden. (I would like not to use jQuery if possible) This I have so far.
function check(){
var div1 = document.getElementById("element1");
var div2 = document.getElementById("element2");
if(div1.top > (window.top + viewport.height )) {
div2.style.display = "none";
}
else {
div2.style.display = "block";
}
}
With this I get the error 'viewport is not defined'. I know it has to be defined but dont really know how to.
You should attach the function to the scroll event on what element is scrolling and checking that scroll position with the end (as bottom position) of your element1 to check.
This is an example:
function check(){
var div1 = document.getElementById("element1");
var div2 = document.getElementById("element2");
if(document.documentElement.scrollTop > (div1.clientTop + div1.clientHeight )) {
div2.style.display = "block";
}
else {
div2.style.display = "none";
}
}
window.onscroll=check;
body{
height: 200vh;
position: relative;
}
#element1{
border: 1px solid green;
width: 200px;
height: 50px;
}
#element2{
border: 1px solid red;
width: 200px;
height: 50px;
position: fixed;
display: none; /*initial display state*/
top: 50vh;
right: 0;
background-color: salmon;
}
<div id="element1"></div>
<div id="element2"></div>
Also, you can check for the Intersection Observer API which is used sometimes for lazy loading images.
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
If you don't plan on supporting really old browsers (like IE11 and below), you can actually use the Intersection Observer API to achieve what you're doing. The advantage is that the API actually contains configurable logic that can further fine tune at what threshold you want to toggle display state of the target element.
In fact, almost 74% of global browser traffic actually supports Intersection Observer. With the notable exception of Safari. Fret not, there is a polyfill available if you need it.
Here is an example:
function callback(entries) {
entries.forEach(function(entry) {
var elementToToggle = document.getElementById('element2');
elementToToggle.style.display = entry.isIntersecting ? 'none' : 'block';
});
}
var observer = new IntersectionObserver(callback);
observer.observe(document.getElementById('element1'));
body {
min-height: 200vh;
position: relative;
}
.element {
width: 50%;
height: 50px;
border: 1px solid rgba(0,0,0,.25);
}
#element1 {
background-color: steelblue;
position: absolute;
top: 100px;
}
#element2 {
background-color: rebeccapurple;
position: fixed;
top: 25;
right: 0;
}
<div class="element" id="element1"></div>
<div class="element" id="element2"></div>

Close modal on esc press (pure JS)

I'm trying to get my modal to close (have a class that sets it to display: block removed) on an esc press. Here's what I tried (which doesn't work and breaks the rest of my code):
if (modal.classList.contains('modal-visible')) {
document.addEventListener('keypress', function(e) {
let keyCode = e.keyCode;
if (keyCode === '27') {
modal.classList.remove('modal-visible');
}
}
Here's the other code from my JS file for the modal:
const modal = document.getElementById('myModal');
const closeIcon = document.querySelector('.close');
// When called, adds class that sets modal to display: block when player reaches water
function bringUpModal() {
modal.classList.add('modal-visible');
}
// Closes modal (adding class that sets it back to display: none) upon user's clicking its close icon
closeIcon.addEventListener('click', function() {
modal.classList.remove('modal-visible');
});
// Opens modal when player reaches water
Player.prototype.update = function(dt) {
if (this.y === 25) {
bringUpModal();
this.y = 400;
}
};
Doubt this is needed, but just in case, here's my CSS for the modal as well:
.modal {
display: none;
width: 33%;
height: 30%;
margin: 0 auto;
background-color: #f1f0f0;
border: 1px solid #c5c4c4;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* Added by function bringUpModal() */
.modal-visible {
display: block;
}
.modal-content {
margin: 0 auto;
padding: 30px;
width: 80%;
display: flex;
flex-direction: column;
justify-content: center;
}
Add an event listener for the modal when the page loads, not if the modal is visible. Also, use the keydown event instead of keypress as in some browsers the keypress event is only fired if the key outputs a character
.modal {
display: none;
width: 33%;
height: 30%;
margin: 0 auto;
background-color: #f1f0f0;
border: 1px solid #c5c4c4;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* Added by function bringUpModal() */
.modal-visible {
display: block;
}
<div id="modal" class="modal modal-visible" style="border: 2px solid black; width: 100px; height: 100px;"></div>
<script>
var modal = document.getElementById("modal");
document.addEventListener('keydown', function(e) {
let keyCode = e.keyCode;
document.getElementById("result").innerHTML = "Key Code: "+keyCode+"<br/> Key: "+e.key+"<br/>";
if (keyCode === 27) {//keycode is an Integer, not a String
modal.classList.remove('modal-visible');
document.getElementById("result").innerHTML += "Escape key pressed. Modal hidden.";
}
});
</script>
<div style="width: 100%; text-align: center;">
<span id="result"></span>
</div>
If you only create the event listener if the modal is visible, it will simply never be created. Making the modal visible afterward will not re-execute your code. The if check has already occurred, already failed, and the event listener for keydown has already not been created. At no point will it be created.
var x = 2;
if (x === 1) {
alert('yes');
}
x = 1;
In the above example, the alert never happens, even though x eventually becomes 1. Similarly, your event listener never gets created.
At the same time, the keyCode is an integer, not a string. You will want to use 27 instead of '27'.

Simple hover controls

So I have few squares, and when I hover over one, i want a menu to show up. Then, when I hover out, i want it to disappear. Simple right?
So the problem is when I move my mouse very fast over them, some of them stay... hidden. I can resign from squares going transparent, but my mouseout event is not fired right too.. because my mouse is far away, and my black menu is still on top of a square!
So fading out pink squares is more to show the issue. I am most troubled by black square not disappearing.
$(document).ready(function() {
$('.square').mouseenter(faceon);
$('#hover_controls').mouseleave(faceout);
});
function faceon() {
$(this).stop().clearQueue().fadeTo("slow", 0.15);
$('#hover_controls').stop().clearQueue().css({
top: $(this).offset().top + "px",
left: $(this).offset().left + "px",
display: 'block'
}).fadeTo("fast", 1);
}
function faceout(event) {
var e = event.toElement || event.relatedTarget;
if (e.parentNode == this || e == this) {
return;
}
$('.square').stop().clearQueue().fadeTo("slow", 1);
$('#hover_controls').stop().clearQueue().fadeTo("fast", 0, function() {
$(this).hide();
});
}
.square {
height: 72px;
width: 72px;
background: pink;
margin: 5px;
display: inline-block;
}
#hover_controls {
display: none;
height: 62px;
width: 62px;
opacity: 0;
padding: 5px;
position: fixed;
background: #000;
border-radius: 10px;
z-index: 2;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='hover_controls'>
<a href='#' onclick='alert("aaa");'>a</a>
<a href='#' onclick='alert("bbbb");'>b</a>
</div>
<div class="list">
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
</div>
Any ideas?
Replace mouseover and mouseout with mouseenter and mouseleave respectively. I hope this helps.
Change the event handler, fix issue wtih e in the conditional if mouse out fast where e is null.
The complication here is the mouseenter/mouseleave and the animation - note that those events are on different elements, one of which you show/hide when the events trigger. Thus it is likely the mouseleave event does not properly trigger ALL the time due to the element it is hooked to not being visible on a "fast mouse" action behavior.
$(document).ready(function() {
$('.square').on("mouseenter", faceon);
$('#hover_controls').on("mouseleave", faceout);
});
function faceon() {
$(this).stop().clearQueue().fadeTo("slow", 0.15);
$('#hover_controls').stop().clearQueue().css({
top: $(this).offset().top + "px",
left: $(this).offset().left + "px",
display: 'block'
}).fadeTo("fast", 1);
}
function faceout(event) {
var e = event.toElement || event.relatedTarget;
if (e && (e.parentNode == this || e == this)) {
return;
}
$('.square').stop().clearQueue().fadeTo("slow", 1);
$('#hover_controls').stop().clearQueue().fadeTo("fast", 0, function() {
$(this).hide();
});
}
.square {
height: 72px;
width: 72px;
background: pink;
margin: 5px;
display: inline-block;
}
#hover_controls {
display: none;
height: 62px;
width: 62px;
opacity: 0;
padding: 5px;
position: fixed;
background: #000;
border-radius: 10px;
z-index: 2;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='hover_controls'>
<a href='#' onclick='alert("aaa");'>a</a>
<a href='#' onclick='alert("bbbb");'>b</a>
</div>
<div class="list">
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
</div>

no scrolling via touch in a box with overflow: scroll / auto in a draggable box

I have a problem with a div with overflow scroll / auto box in a second div box which is draggable.
On my desktop PC, I can scroll the inner box without problems on my iPad when trying to scroll the outer box moves.
I use the draggable from jQuery UI library
Here is an example:
http://jsfiddle.net/3rfjB/1/
HTML:
<div id="outer">
<div id="inner">
A<br/>B<br/>C<br/>D<br/>E<br/>F<br/>G<br/>H
</div>
</div>
JS:
$('#outer').draggable();
CSS:
#outer{
position: fixed;
top: 20px;
left: 20px;
width: 400px;
height: 300px;
border: 1px solid black;
}
#inner{
position: absolute;
top: 10px;
left: 10px;
width: 200px;
height: 100px;
overflow: auto;
border: 1px solid red;
}
Thanks in advance for your advice.
regards
yannic
I have found a solution to my problem:
I have the scroll by touch for the box itself implemented.
var TOUCH_MOVE_TEST_X = undefined;
var TOUCH_MOVE_TEST_Y = undefined;
$('#inner').bind('touchstart', function(event){
TOUCH_MOVE_TEST_X = event.originalEvent.touches[0].clientX;
TOUCH_MOVE_TEST_Y = event.originalEvent.touches[0].clientY;
return false;
});
$('#inner').bind('touchmove', function(event){
event.srcElement.offsetParent.offsetParent.scrollLeft = event.srcElement.offsetParent.offsetParent.scrollLeft + ( TOUCH_MOVE_TEST_X - event.originalEvent.touches[0].clientX );
TOUCH_MOVE_TEST_X = event.originalEvent.touches[0].clientX;
event.srcElement.offsetParent.offsetParent.scrollTop = event.srcElement.offsetParent.offsetParent.scrollTop + ( TOUCH_MOVE_TEST_Y - event.originalEvent.touches[0].clientY );
TOUCH_MOVE_TEST_Y = event.originalEvent.touches[0].clientY;
return false;
});
$('#inner').bind('touchend', function(event){
TOUCH_MOVE_TEST_X = undefined;
TOUCH_MOVE_TEST_Y = undefined;
return false;
});
best regards
yannic

Categories

Resources