Code stops working when I change the EventListener from "click" to "keydown" - addeventlistener

The piece of code below sucessfully manages to change the position of a block within a grid when you click on it ( on the grid ).
However , I intend to do it when I press on a key. It happens that when I change the EventListener from "click" to "keydown" I get no response if I press on any key of my laptop while running the script.
I assume something is wrong with the element (I need to focus it? How ?) , or the document somehow is unable to recognize the key input. What can I do to try fix this problem ?
let x = 5;
let y = 5;
function CreateSnake(){
tabuleiro.appendChild(snake);
snake.style.gridColumnStart = x;
snake.style.gridRowStart = y;
snake.classList.add("snake");
};
tabuleiro.addEventListener("click" , function(){
moveSnakeRight();
remove()
});
function moveSnakeRight(){
if ( x < 10)
console.log(x++)
;
}
function remove(){
snake.remove()
}

My guess is that you've got the keydown event listener on the wrong element. In order to handle the keydown event, the element must be able to receive focus (setting tabindex). See the following demo: https://jsfiddle.net/mqe59dL6/
const noFocus = document.getElementById('noFocus');
const focus = document.getElementById('focus');
function handler(event) {
event.cancelBubble = true;
console.log(event.target.id || 'document', event.keyCode);
}
noFocus.addEventListener('keydown', handler);
focus.addEventListener('keydown', handler);
document.addEventListener('keydown', handler);
#noFocus {
height: 60px;
background-color: #aff;
padding: 10px;
}
#focus {
height: 60px;
background-color: #faf;
padding: 10px;
}
<div id="noFocus">
I can't receive focus. Events will go to the document.
</div>
<div id="focus" tabindex="0">
I can receive focus, click on me first.
</div>
An alternative is to handle the event on the document instead. Try document.addEventListener("keydown", ...).

Related

How to make fixed-content go above iOS keyboard?

I can only find questions where people have the opposite problem.
I want my fixed content to go above the iOS keyboard.
Image of the problem:
I want iOS to behave like Android.
Is there a simple way to achieve this?
Parent element css:
.parent{
position:fixed;
top: 0;
left 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
Button css:
.button{
position:fixed;
left 0;
right: 0;
bottom: 0;
width: 100%;
height: 5rem;
}
We can use VisualViewport to calculate keyboard height. So we can set fixed-content pos correct.
Small demo: https://whatwg6.github.io/pos-above-keyboard/index.html
Code snippet:
const button = document.getElementById("button");
const input = document.getElementById("input");
const height = window.visualViewport.height;
const viewport = window.visualViewport;
window.addEventListener("scroll", () => input.blur());
window.visualViewport.addEventListener("resize", resizeHandler);
function resizeHandler() {
if (!/iPhone|iPad|iPod/.test(window.navigator.userAgent)) {
height = viewport.height;
}
button.style.bottom = `${height - viewport.height + 10}px`;
}
function blurHandler() {
button.style.bottom = "10px";
}
html,
body {
margin: 0;
padding: 0;
}
#button {
position: fixed;
width: 100%;
bottom: 10px;
background-color: rebeccapurple;
line-height: 40px;
text-align: center;
}
<input type="text" inputmode="decimal" value="0.99" id="input" onblur="blurHandler()" />
<div id="button">Button</div>
Problems: https://developers.google.com/web/updates/2017/09/visual-viewport-api#the_event_rate_is_slow
Why not innerHeight?: Iphone safari not resizing viewport on keyboard open
Mobile Safari does not support position: fixed when an input focused and virtual keyboard displayed.
To force it work the same way as Mobile Chrome, you have to use position: absolute, height: 100% for the whole page or a container for your pseudo-fixed elements, intercept scroll, touchend, focus, and blur events.
The trick is to put the tapped input control to the bottom of screen before it activates focus. In that case iOS Safari always scrolls viewport predictably and window.innerHeight becomes exactly visible height.
Open https://avesus.github.io/docs/ios-keep-fixed-on-input-focus.html in Mobile Safari to see how it works.
Please avoid forms where you have several focusable elements because more tricks to fix position will be necessary, those were added just for demonstration purposes.
Note that for rotation and landscape mode, additional tricks are necessary. I'm working on a framework called Tuff.js which will provide a full-screen container helping mobile web developers to build web applications much faster. I've spend almost a year on the research.
By the way, to prevent scrolling of the whole window when virtual keyboard is active, you can use this super simple trick
var hack = document.getElementById('scroll-hack');
function addScrollPixel() {
if (hack.scrollTop === 0) {
// element is at the top of its scroll position, so scroll 1 pixel down
hack.scrollTop = 1;
}
if (hack.scrollHeight - hack.scrollTop === hack.clientHeight) {
// element is at the bottom of its scroll position, so scroll 1 pixel up
hack.scrollTop -= 1;
}
}
if (window.addEventListener) {
// Avoid just launching a function on every scroll event as it could affect performance.
// You should add a "debounce" to limit how many times the function is fired
hack.addEventListener('scroll', addScrollPixel, true);
} else if (window.attachEvent) {
hack.attachEvent('scroll', addScrollPixel);
}
body {
margin: 0 auto;
padding: 10px;
max-width: 800px;
}
h1>small {
font-size: 50%;
}
.container {
display: flex;
align-items: top;
justify-content: space-between;
}
.container>div {
border: #000 1px solid;
height: 200px;
overflow: auto;
width: 48%;
-webkit-overflow-scrolling: touch;
}
<h1>iOS Scroll Hack</h1>
<p>Elements with overflow:scroll have a slightly irritating behaviour on iOS, where when the contents of the element are scrolled to the top or bottom and another scroll is attempted, the browser window is scrolled instead. I hacked up a fix using minimal,
native JavaScript.</p>
<p>Both lists have standard scrolling CSS applied (<code>overflow: auto; -webkit-overflow-scrolling: touch;</code>), but the list on the right has the hack applied. You'll notice you can't trigger the browser to scroll whilst attempting to scroll the list
on the right.</p>
<p>The only very slight drawback to this is the slight "jump" that occurs when at the top or bottom of the list in the hack.</p>
<div class='container'>
<div id='scroll-orig'>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
<li>16</li>
<li>17</li>
<li>18</li>
<li>19</li>
<li>20</li>
</ul>
</div>
<div id='scroll-hack'>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
<li>16</li>
<li>17</li>
<li>18</li>
<li>19</li>
<li>20</li>
</ul>
</div>
</div>
Got this answer from here
This is a well known problem, and unfortunately one must resort to hacky tricks like the accepted answer for now. The W3C is however in the process of specifying The VirtualKeyboard API.
Note: At the time of writing, this answer is not yet ready for prime time. It's important to understand that this specification must also be forward looking, to adapt to the myriad possible virtual keyboards of the future. It may be a few years before reliable cross platform browser support begins to appear and this answer becomes the correct one.
I found an interesting solution to this problem.
The solution is to create a hidden input and focus on it on the touchstart event.
<input id="backinput" style="position:absolute;top:0;opacity:0;pointer-events: none;">
<input id="input" style="position:absolute;bottom:0;">
Using JQuery:
$('#backinput').on('focus',function(e)
{
e.preventDefault();
e.stopPropagation();
const input = document.getElementById('input');
input.focus({ preventScroll: true });
})
$('#input').on("touchstart", function (event) {
if(!$(this).is(":focus"))
{
event.stopPropagation();
event.preventDefault();
$('#backinput').focus();
}
})
Finally, resize the viewport so that the bottom input moves above the keyboard (if needed)
window.visualViewport.addEventListener("resize", (event) => {
$('body').height(parseInt(visualViewport.height));
});
For me it works perfect. I am building a messenger.

Dismiss TableSorter Column Selector Widget

I'm using the Column Selector Widget on Mottie's excellent tablesorter. It works great!
However, when a user clicks on the button (using CSS Popup only mode), the only way to dismiss the selection modal is to click on the same button again. This is inconsistent with the rest of my app, which dismisses bootstrap modals when clicking anywhere outside of the modal.
I know I can write an onClick function to monitor the whole body, but I wonder, is there a built-in option that I've missed that will dismiss the column chooser when a user clicks outside the box?
That "Column" button uses a hidden checkbox to show/hide the popup - it's pure HTML & CSS and completely customizable.
If you to modify the current setup, add the following (demo):
HTML (add after the "columnSelectorWrapper")
<div id="columnSelectorOverlay"></div>
CSS
#columnSelectorOverlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,.5);
display:none;
}
* note: remove the background: rgba(0,0,0,.5); setting if you don't want the dark overlay.
Then add the following setting to the .columnSelector definition:
z-index: 1;
Then add this javascript
$('#colSelect1').on('change', function() {
if (this.checked) {
$('#columnSelectorOverlay').show();
}
});
$('#columnSelectorOverlay').click(function() {
$('#colSelect1').prop('checked', false);
$(this).hide();
});

Gmail like listview item removing - jquerymobile

I'm building a phonegap mobile app with jqm 1.3.
I have a listview element, each list item have 2 actions, rename when swipe right, and delete when swipe left. What I want to achieve is a behavior like in gmail mobile application. When some list item is dragged aside (more than some threshold), another "layer" is shown with related buttons. currently I'm using code from jquery mobile swipe list demo, with popup on swipe event, but it is not fulfill my needs.
How this stuff can be implemented ?
Is there any plugin to achieve that functionality?
I tried to make something like this. The working demo is here - http://jsfiddle.net/Q9htn/19/
First HTML:
<ul id="list" data-role="listview"></ul>
Then some CSS. I am not very happy with having to define row height this way and I am sure there must be better ways how to do this fully dynamically, but hopefully it should be OK for this purpose. It makes sure that the row stays as it is during the animations which happen.
.row {
height: 1em;
}
.item {
position: absolute;
display: inline-block;
width: 100%;
right: -1em; /* This makes the item to fly out to the right */
}
.menu {
width: 100%;
display: inline-block;
text-align: center;
}
JavaScript:
var items = ["Audi", "Mercedes", "Skoda", "Rover", "Nisan", "Mazda", "Toyota"];
$.each(items, function(index, item) {
var li = $("<li class='row'>");
var contents = $("<span class='item'>" + item + "</span>");
contents.attr("data-customid", index); // Set some id
li.append(contents);
$("#list").append(li);
});
$("#list").listview("refresh");
// Attach swiperight handler on the list
$("#list").on("swiperight",">li",function(e){
var li = $(this);
var contents = $(li.children()[0]);
var item = contents.text(); // Get the item value
var itemId = contents.attr("data-customid");
var delButton = $("<a>").text("Yes").click(function(e){
// Delete handler, fade out menu and remove the row
menu.fadeOut(function(){
li.remove();
alert("Deleted " + item + " with ID = " + itemId);
});
});
var cancelButton = $("<a>").text("No").click(function(e){
// Cancel Handler, remove menu and show the item
menu.fadeOut(function(){
contents.animate({width: 'toggle'}, function(){
menu.remove();
});
});
});
// Create the menu
var menu = $("<span />").append("Sure? - ").append(delButton).append(" | ").append(cancelButton)
.css("display", "none")
.addClass("menu");
// Insert the menu
contents.after(menu);
// Slide the item
contents.animate({width: 'toggle'}, function(){
// And fade in the menu
menu.fadeIn();
});
});

How to change jquery ui datepicker position?

Is it possible to change top and left positions (get current values and change them) of jQuery UI datepicker. Please note that i need to change position, not set margin as it is in other examples.
Sure it is. As there's always only one datepicker active, you can select active datepicker with:
var $datepicker = $('#ui-datepicker-div');
and change its position:
$datepicker.css({
top: 10,
left: 10
});
EDIT
Whoah, tricky one. If you set top or left position in beforeShow, it gets overriden again by datepicker plugin. You have to put css changes in a setTimeout:
$("#datepicker").datepicker({
beforeShow: function (input, inst) {
setTimeout(function () {
inst.dpDiv.css({
top: 100,
left: 200
});
}, 0);
}
});
DEMO: http://jsfiddle.net/BWfwf/4/
Explanation about setTimeout(function () {}, 0): Why is setTimeout(fn, 0) sometimes useful?
If you get really stuck you can edit your jquery-ui-[version].custom.js. The function that controls the position where the calender will appear is:
_findPos: function(obj) {
var position,
inst = this._getInst(obj),
isRTL = this._get(inst, "isRTL");
while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
obj = obj[isRTL ? "previousSibling" : "nextSibling"];
}
position = $(obj).offset();
return [position.left, position.top];
},
I have some custom code that uses a CSS3 transformation to zoom the page in or out based on its width. This throws out the screen coordinates that the calendar widget relies on. I added some custom code to the _findPos to detect and handle the zoom level. Modified code looks like this:
_findPos: function(obj) {
var position,
inst = this._getInst(obj),
isRTL = this._get(inst, "isRTL");
while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
obj = obj[isRTL ? "previousSibling" : "nextSibling"];
}
position = $(obj).offset();
/* Custom Code for Zoom */
var zoomLevel = 1;
var minW = 1024;
if ($(window).width() > minW)
{ zoomLevel = $(window).width() / minW;}
return [position.left, position.top/zoomLevel];
},
May be an old question, but ran into the problem myself just today and could not get other suggestions to work. Fixed it alternatively (using .click(function(){}) and wanted to add my two cents.
I have an input field with the id sDate which, when clicked, displays the datepicker.
What I did to solve the problem was add a click routine to the #sDate field.
$('#sDate').click(function(){ //CHANGE sDate TO THE ID OF YOUR INPUT FIELD
var pTop = '10px'; //CHANGE TO WHATEVER VALUE YOU WANT FOR TOP POSITIONING
var pLeft = '10px'; //CHANGE TO WHATEVER VALUE YOU WANT FOR LEFT POSITIONING
$('#ui-datepicker-div').css({'left':pLeft, 'top':pTop});
});
your solution works provided you run it after calling the datepicker in the code, I tried calling it before but it didn't work, so I tried to understand how it worked for you.
I have adapted the datepicker in the context of an input field which is fixed at the top of the page to scroll. The datepicker was lost ...
Here is my example code for adaptation in a context of datepicker which becomes dynamically fixed:
Example found on w3schools.com: https://www.w3schools.com/howto/howto_js_sticky_header.asp
HTML:
<div class="padding-16 center" id="resa_nav">
<div style="margin: 24px 0 0;">
<label for="date_selector"
class="text-gray">Choose a date</label>
<input type="text" id="date_selector" name="date_selector" class="padding-small">
</div>
</div>
CSS:
.sticky {
position: fixed;
top: 0;
width: inherit;
background: white;
box-shadow: 0px 0px 7px 4px #69696969;
}
JS:
// init datepicker
$('#date_selector').datepicker();
// When the user scrolls the page, execute myFunction
window.onscroll = function() { myFunction() };
// Get the header
var header = document.getElementById('resa_nav');
// Get the offset position of the navbar
var sticky = header.offsetTop;
// Add the sticky class to the header when you reach its scroll position. Remove "sticky" when you leave the scroll position
function myFunction() {
if (window.pageYOffset > sticky) {
// set block sticky
header.classList.add('sticky');
// adjust datepicker position
// attach a namespace for unbind click later in "non-sticky" context
$('#date_selector').on('click.sticked', function(){
var top = '10px';
var left = '10px';
$('#ui-datepicker-div').css({'left': left, 'top': top});
});
}
else {
// remove sticky
header.classList.remove('sticky');
// unbind the second event 'click' for retrieve the
// default of settings in "non-sticky" context
$('#date_selector').off('click.sticked');
}
}
// END FUNCTION
hope to help!
just add css as below for datepicker
.datepicker {
top: -150px;
/* adjust value as per requirement. if not work try with addin !important*/
}

jQuery mobile image gallery default vertical scroll issue

I have an image gallery with a series of thumbs. Both are loaded dynamically depending on how many images are associated with a product. You can click on a thumb to get that series of images. On a mobile device you can swipe left and right to see all the images. That all works fine.
Here's my issue: When you try to scroll down the page and you happen to be touching one of the main images you get stuck and the page doesn't move. For the life of me I cannot figure out a work around for this. I am wondering if anyone has ever encountered this and figured out a solution. I think there could be a way to control this via the touch-punch.js but cannot figure it out. Thank you.
Here is my simplified gallery HTML:
<ul id="Gallery" class="gallery productGalleryInner">
<li>
<a href="#"><img src="img.jpg" />/a>
<a href="#"><img src="img.jpg" />/a>
<a href="#"><img src="img.jpg" />/a>
<a href="#"><img src="img.jpg" />/a>
<a href="#"><img src="img.jpg" />/a>
</li>
</ul>
</div>
css:
#productGalleryWrap {
overflow: hidden;
margin: 7px 10px 0;
}
#productGalleryWrap .productGalleryInner {
white-space: nowrap;
}
#productGalleryWrap .productGalleryInner li {
display: inline;
}
#productGalleryWrap .productGalleryInner img {
display: inline;
width: 50%;
}
I am using touch-punch.js to control the horizontal drag. Here is the code:
(function ($) {
// Detect touch support
$.support.touch = 'ontouchend' in document;
// Ignore browsers without touch support
if (!$.support.touch) {
return;
}
var mouseProto = $.ui.mouse.prototype,
_mouseInit = mouseProto._mouseInit,
touchHandled;
/**
* Simulate a mouse event based on a corresponding touch event
* #param {Object} event A touch event
* #param {String} simulatedType The corresponding mouse event
*/
function simulateMouseEvent (event, simulatedType) {
// Ignore multi-touch events
if (event.originalEvent.touches.length > 1) {
return;
}
event.preventDefault();
var touch = event.originalEvent.changedTouches[0],
simulatedEvent = document.createEvent('MouseEvents');
// Initialize the simulated mouse event using the touch event's coordinates
simulatedEvent.initMouseEvent(
simulatedType, // type
true, // bubbles
true, // cancelable
window, // view
1, // detail
touch.screenX, // screenX
touch.screenY, // screenY
touch.clientX, // clientX
touch.clientY, // clientY
false, // ctrlKey
false, // altKey
false, // shiftKey
false, // metaKey
0, // button
null // relatedTarget
);
// Dispatch the simulated event to the target element
event.target.dispatchEvent(simulatedEvent);
}
/**
* Handle the jQuery UI widget's touchstart events
* #param {Object} event The widget element's touchstart event
*/
mouseProto._touchStart = function (event) {
var self = this;
// Ignore the event if another widget is already being handled
if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
return;
}
// Set the flag to prevent other widgets from inheriting the touch event
touchHandled = true;
// Track movement to determine if interaction was a click
self._touchMoved = false;
// Simulate the mouseover event
simulateMouseEvent(event, 'mouseup');
// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
// Simulate the mousedown event
simulateMouseEvent(event, 'mousedown');
};
mouseProto._touchMove = function (event) {
// Ignore event if not handled
if (!touchHandled) {
return;
}
// Interaction was not a click
this._touchMoved = true;
// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
};
/**
* Handle the jQuery UI widget's touchend events
* #param {Object} event The document's touchend event
*/
mouseProto._touchEnd = function (event) {
// Ignore event if not handled
if (!touchHandled) {
return;
}
// Simulate the mouseup event
simulateMouseEvent(event, 'mouseup');
// Simulate the mouseout event
simulateMouseEvent(event, 'mouseout');
// If the touch interaction did not move, it should trigger a click
if (!this._touchMoved) {
// Simulate the click event
simulateMouseEvent(event, 'click');
}
// Unset the flag to allow other widgets to inherit the touch event
touchHandled = false;
};
/**
* A duck punch of the $.ui.mouse _mouseInit method to support touch events.
* This method extends the widget with bound touch event handlers that
* translate touch events to mouse events and pass them to the widget's
* original mouse event handling methods.
*/
mouseProto._mouseInit = function () {
var self = this;
// Delegate the touch handlers to the widget's element
self.element
.bind('touchstart', $.proxy(self, '_touchStart'))
.bind('touchmove', $.proxy(self, '_touchMove'))
.bind('touchend', $.proxy(self, '_touchEnd'));
// Call the original $.ui.mouse init method
_mouseInit.call(self);
};
})(jQuery);
Please let me know if more info is needed. Thanks.
Kind of late answer but I had this same problem on a current project I'm working on.
Comment out:
event.preventDefault();
Add this after:
document.ontouchmove = function(e) {
var target = e.currentTarget;
while(target) {
if(checkIfElementShouldScroll(target))
return;
target = target.parentNode;
}
e.preventDefault();
};
Dolla dolla bills y'all.
Code taken from this thread: document.ontouchmove and scrolling on iOS 5

Resources