So, I try to animate browser window in electron app.
I try to use this code to do this:
const fadeWindowOut = (
browserWindowToFadeOut,
step = 0.1,
fadeEveryXSeconds = 10
) => {
let opacity = browserWindowToFadeOut.getOpacity();
const interval = setInterval(() => {
if (opacity <= 0) window.clearInterval(interval);
browserWindowToFadeOut.setOpacity(opacity);
opacity -= step;
}, fadeEveryXSeconds);
return interval;
}
It work's correctly but I can't handle user click to icon in the taskbar.
When user clicks to icon in the taskbar, window minimized or restored by default.
I can catch events 'minimize', but I can't prevent default and do my animation before default.
// it doesn't work
mainWin.on('minimize', async (event) => {
event.preventDefault();
await doMyAnimationHere();
mainWin.minimize();
});
So, how can I run my animation before default 'minimize' event?
Related
I am trying to set a loader for the data loading section. but, I already have a loader for before loading full website data.
$(window).on("load", function () {
// Animate loader off screen
$(".se-pre-con").fadeOut("slow");
});`
And now I set this, to load the data section.
const npreloader = document.querySelector('.npreloader');
const fadeEffect = setInterval(() => {
// if we don't set opacity 1 in CSS, then
// it will be equal to "", that's why we
// check it
if (!npreloader.style.opacity) {
npreloader.style.opacity = 1;
}
if (npreloader.style.opacity > 0) {
npreloader.style.opacity -= 0.1;
} else {
clearInterval(fadeEffect);
}
}, 500);
window.addEventListener('load', ()=> fadeEffect);
Can I replace the event keyword "load" with any event? Because it overrides each other.
I'm working on building an infinite page-scroll, at least until it reaches the last page. However, it scrolls way to quickly and overloads my web-browser. I wanted to know how I can implement a time-lag on each scroll, something like 1-2 seconds between the scrolls.
Here's what I have developed so far:
var infScroll = setInterval(function () {
var scrollingElement = (document.scrollingElement || document.body);
scrollingElement.scrollTop = scrollingElement.scrollHeight;
}, 200);
It works and keeps scrolling down the page, however, how do I set a lag between each scroll?
I have tried updating it with a timer as the following:
function disableScroll() {
// temporarily disable action
scrollEnabled = false;
// set a timer to enable again it 1 second from now
setTimeout(function() {
scrollEnabled = true;
}, 8000);
}
var infScroll = setInterval(function () {
var scrollingElement = (document.scrollingElement || document.body);
scrollingElement.scrollTop = scrollingElement.scrollHeight; disableScroll()
}, 200);
However, the function won't stop the scroll at all for a few moments.
I want to have a fast page-load but also not have the user see a delay before content is rendered while scrolling down the page.
I have a large html page with many <dl> elements.
To speed up page load, I have set content-visibility: auto in css. See https://web.dev/content-visibility/
dl {
content-visibility: auto;
contain-intrinsic-size: 1000px;
}
Due to the complexity of the contents of the <dl>s there is a noticeable delay when a user scrolls while the <dl>s are rendered as they come into the viewport.
Thus, soon after page-load I want to render all the offscreen <dl> straight away (before a user scrolls to them) but in such a way that it does not block the main thread and scrolling remains responsive.
So, I want to set content-visibility: visible on the <dl>s starting from the top one, and not blocking the main thread (for more than say 50ms). So, maybe allowing user interaction after rendering each <dl>.
So, I need a version of the below, that doesn't block the main thread:
document.querySelectorAll('dl').forEach(function(dlElement, currentIndex, listObj) { dlElement.style['content-visibility'] = 'visible' });
My use case: My page is of math notes, which I want all on one page to reduce friction. I use katex which (for now, before we can use mathml on chrome) produces very large and complex html, which even server-side rendered still takes a lot of time for layout and rendering on the browser.
Rather than leave this unanswered, let me paste my (unperfected) code I have been testing for the last few weeks.
// --------- Shim requestIdleCallback if not supported in browser ----------------------------------
window.requestIdleCallback =
window.requestIdleCallback ||
function (cb) {
var start = Date.now();
return setTimeout(function () {
cb({
didTimeout: false,
timeRemaining: function () {
return Math.max(0, 50 - (Date.now() - start));
}
});
}, 1);
}
window.cancelIdleCallback =
window.cancelIdleCallback ||
function (id) {
clearTimeout(id);
}
// Global
let isRequestIdleCallbackScheduled = false;
let nodeToRemove = null;
let isVisualUpdateScheduled = false;
let totalDlElementsLeftToShow = 0;
function startIfNeeded() {
totalDlElementsLeftToShow = document.querySelectorAll('dl:not([style*="content-visibility: visible;"]), .ra:not([style*="content-visibility: visible;"]').length;
if (totalDlElementsLeftToShow > 0) {
console.log('Not a mobile - Let\'s make things visible when we are next idle');
scheduleVisibilityChanges();
}
else {
console.log('Apparently, all visible');
}
}
function scheduleVisibilityChanges() {
// Only schedule the rIC if one has not already been set.
if (isRequestIdleCallbackScheduled) {
//console.log('returning because idle callback scheduled');
startIfNeeded();
}
isRequestIdleCallbackScheduled = true;
//console.log('scheduling visibility changes when next idle or in 30 seconds at the latest');
requestIdleCallback(processHiddenElements, { timeout: 30000 });
}
function processHiddenElements(deadline) {
// Since our operation takes a while, we only want to go ahead if we have at least 50ms.
while (deadline.timeRemaining() > 49 && totalDlElementsLeftToShow > 0) {
// console.log('time remaining is ', deadline.timeRemaining(), '- scheduling next visual update');
// Don't set content-visibility immediately wait for the next
// requestAnimationFrame callback.
scheduleVisualUpdateIfNeeded();
}
// console.log('Deadline reached, will check again the next time the user is idle if there are more events still to send');
if (totalDlElementsLeftToShow > 0) {
requestIdleCallback(processHiddenElements, { timeout: 30000 });
}
}
function scheduleVisualUpdateIfNeeded() {
if (isVisualUpdateScheduled) {
// console.log('returning - visual update already scheduled')
return;
};
isVisualUpdateScheduled = true;
// console.log('requesting animation frame');
requestAnimationFrame(setContentToVisible);
}
function setContentToVisible() {
// console.log('changing visibility of element ');
let completeHiddenNodeList = document.querySelectorAll('dl:not([style*="content-visibility: visible;"]), .ra:not([style*="content-visibility: visible;"]');
// We chunk the layout changes
let i;
let numberToChunk = 20;
if (completeHiddenNodeList.length < 20) {
numberToChunk = completeHiddenNodeList.length
}
for (i = 0; i < numberToChunk; ++i) {
completeHiddenNodeList[i].style.contentVisibility = 'visible';
}
isVisualUpdateScheduled = false;
isRequestIdleCallbackScheduled = false;
totalDlElementsLeftToShow = totalDlElementsLeftToShow - numberToChunk;
}
if (!navigator.userAgentData.mobile) {
startIfNeeded();
}
Click event is working fine when using mouse with computer. Even when I put mouse button down on button move cursor and then release mouse button inside button area, click event is firing. But same with touchscreen it is not working. I know that reason is that in touchscreen that kind of dragging is considered as scrolling. Click event is fired when I don't move finger too much on button. So only down and up without moving. My client has problem that they are moving finger too much and it is too hard to get click event. Is it possible to set bigger threshold for how much finger can move that it is still considered as click and not scroll?
I found this article where touch events are handled byself and translated them to click event. http://phonegap-tips.com/articles/fast-touch-event-handling-eliminate-click-delay.html I would not to like to go this road.
Have you any suggestion how can I solve this?
Here is more detail about touch events https://developer.mozilla.org/en-US/docs/Web/API/Touch_events Look at Handling clicks there is described how click is working in touchscreens. Still I didn't managed to work. Few months ago I but evt.preventDefault(); to my touchmove event handler and it did fix problem but currently it seems not.
EDIT:2019.11.5
Here is what was working earlier but no anymore:
html
<body (touchmove)="touchMoveEvent($event)"></body>
TypeScript
touchMoveEvent(ev: Event): void
{
ev.preventDefault();
}
And here is basic angular example of button and click handler which is not working if user is moving finger too much. I haven't check what is threshold but my I assume it is something near 10px-20px.
<button (click)="onClickEventHandler($event)">Press button</button>
onClickEventHandler(ev: Event) {
//do the thing here
}
I have tested touchscreen functionality with chrome's devtools toggle device toolbar.
Here is a nice solution. by using the touchstart and touchend events you can measure the distance between the 2 points and fire a click event if the events where close (in terms of pixels). read my comments.
class ScrollToClick {
constructor(elem, maxDistance = 20) {
this.elem = elem;
this.start = null;
this.maxDistance = maxDistance;
// Bind the touches event to the element
this.bindTouchEvents();
}
bindTouchEvents() {
this.elem.addEventListener('touchstart', this.onTouchStart.bind(this), false);
this.elem.addEventListener('touchend', this.onTouchEnd.bind(this), false);
}
onTouchStart(e) {
// hold the touch start position
this.start = e.touches[0];
// clear the position after 2000 mil (could be set for less).
setTimeout(() => { this.start = null; }, 2000);
}
onTouchEnd(e) {
// if the timeout was called, there will be no start position
if (!this.start) { return; }
// calculate the distance between start and end position
const end = e.changedTouches[0],
dx = Math.pow(this.start.pageX - end.pageX, 2),
dy = Math.pow(this.start.pageY - end.pageY, 2),
distance = Math.round(Math.sqrt(dx + dy));
// if the distance is fairly small, fire
// a click event. (default is 20 but you can override it through the constructor)
if (distance <= this.maxDistance) {
this.elem.click();
}
// clear the start position again
this.start = null;
}
}
Then you can use it with any element like so:
// use any element you wish (here I'm using the body)
const elem = document.body;
// initialize the class with the given element
new ScrollToClick(elem);
// listen to a click event on this element.
elem.addEventListener('click', (e) => {
console.log('Clicked');
})
This is a GitHub Issue that seems to be similar. I am not a JS dev so I am not sure but hope this helps.
My final solution is here. I forgot to mention in text that I am using Angular although I but in tag.
So I made Angular directive and but in AfikDeri's suggestion which was really close with directive style code.
import { Directive, ElementRef, Input, OnInit } from '#angular/core';
#Directive({
selector: '[touchClick]'
})
export class TouchClickDirective implements OnInit {
#Input() maxDistance = 100;
#Input() maxTime = 2000;
#Input() touchClick: boolean;
start: Touch;
constructor(private elem: ElementRef) {
this.start = null;
}
ngOnInit(): void {
// Bind the touches event to the element
this.bindTouchEvents();
}
bindTouchEvents() {
this.elem.nativeElement.addEventListener('touchstart', this.onTouchStart.bind(this), false);
this.elem.nativeElement.addEventListener('touchend', this.onTouchEnd.bind(this), false);
}
onTouchStart(e: TouchEvent) {
// hold the touch start position
this.start = e.touches[0];
// clear the position after 2000 mil (could be set for less).
setTimeout(() => {
this.start = null;
}, this.maxTime);
}
onTouchEnd(e: TouchEvent) {
// if the timeout was called, there will be no start position
if (!this.start) {
return;
}
// calculate the distance between start and end position
const end = e.changedTouches[0],
dx = Math.pow(this.start.pageX - end.pageX, 2),
dy = Math.pow(this.start.pageY - end.pageY, 2),
distance = Math.round(Math.sqrt(dx + dy));
// if the distance is fairly small, fire
// a click event. (default is 20 but you can override it through the constructor)
if (distance <= this.maxDistance) {
this.elem.nativeElement.click();
}
// clear the start position again
this.start = null;
}
}
And here is how it can be used
<button mat-flat-button [touchClick] [maxDistance]="100" [maxTime]="300" (click)="doWarning()">
Generate Warning
</button>
I worked out a quick solution to this problem based only on external value state set on different event listeners. Btn click fn will be triggered on touchend event if moveState variable will not change value by touchmove event. Touch start is always resetting state.
const moveState = false;
btn.addEventListener("click", (e) => handleBtnClick(e));
btn.addEventListener("touchstart", (e) => handleBtnTouchStart(e));
btn.addEventListener("touchmove", (e) => handleBtnTouchMove(e));
btn.addEventListener("touchend", (e) => handleBtnClick(e));
function handleHotspotTouchStart(e){
moveState = false;
}
function handleHotspotTouchMove(e){
moveState = true;
}
function handleBtnClick(e){
e.preventDefault;
if(e.type === 'touchend'){
if(moveState) return;
}
// trigger btn click action for both cursor click and touch if no movement detected
btnClick();
}
To add to the accepted answer, here is my react implementation:
import React, { useState } from 'react';
import './Button.css';
interface ButtonProps {
className: string,
value: string,
icon?: string,
onClick: () => void,
onPointerDown?: () => void,
onPointerUp?: () => void,
style?: React.CSSProperties,
}
function Button(props: ButtonProps): JSX.Element {
const [touchStart, setTouchStart] = useState(null);
const onTouchStart = (e) => {
// store the touchStart position
setTouchStart(e.touches[0]);
// clear the position after 2000ms
setTimeout(() => setTouchStart(null), 2000);
};
const onTouchEnd = (e) => {
// if the timeout was called, there will be no touchStart position
if (!touchStart) return;
// calculate the distance between touchStart and touchEnd position
const touchEnd = e.changedTouches[0],
dx = Math.pow(touchStart.pageX - touchEnd.pageX, 2),
dy = Math.pow(touchStart.pageY - touchEnd.pageY, 2),
distance = Math.round(Math.sqrt(dx + dy));
// if the distance is fairly small, fire a click event.
if (distance <= 50 && distance > 5) {
props.onClick();
}
// clear the start position again
setTouchStart(null);
};
return (
<button
className={`${props.className}`}
onClick={props.onClick}
onPointerDown={props.onPointerDown}
onPointerUp={props.onPointerUp}
onTouchStart={onTouchStart}
onTouchEnd={onTouchEnd}
style={props.style}
>
{props.icon ? <img className="button__icon" src={props.icon} alt=""/> : ''}
{props.value}
</button>
);
}
export default Button;
I want to achieve the double click event on a specific div like this:
<div id="divID" ondblclick = 'alert("double click!!");' >
it worked on the google chrome browser but when I open it with phone it didn't work, by the way the single click worked.
ps: i added this two things
<meta name="viewport" content="width=device-width, initial scale=1,user-scalable=no">
and this
body {
-ms-touch-action: manipulation;
touch-action: manipulation;}
but it didnt work!
I got the same issue. On touch devices, if you want to detect a double-tap gesture and you use the ondblclick event in most cases it will not work and also the problem is it will also fire an onclick. One of the solution is to implement a double tap detection pattern using the following code sample:
var doubletapDeltaTime_ = 700;
var doubletap1Function_ = null;
var doubletap2Function_ = null;
var doubletapTimer = null;
function tap(singleTapFunc, doubleTapFunc) {
if (doubletapTimer==null) {
// First tap, we wait X ms to the second tap
doubletapTimer_ = setTimeout(doubletapTimeout_, doubletapDeltaTime_);
doubletap1Function_ = singleTapFunc;
doubletap2Function_ = doubleTapFunc;
} else {
// Second tap
clearTimeout(doubletapTimer);
doubletapTimer_ = null;
doubletap2Function_();
}
}
function doubletapTimeout() {
// Wait for second tap timeout
doubletap1Function_();
doubleTapTimer_ = null;
}
And you can call it like
<div id="divID" onclick="tap(tapOnce, tapTwice)" >
tapOnce and tapTwice are your functions which will be called in respective cases. This solution will work on browsers too.
Reference
Here is the external function 'doubletap' which can be helpful:
/*
* jQuery Double Tap
* Developer: Sergey Margaritov (sergey#margaritov.net)
* Date: 22.10.2013
* Based on jquery documentation http://learn.jquery.com/events/event-extensions/
*/
(function($){
$.event.special.doubletap = {
bindType: 'touchend',
delegateType: 'touchend',
handle: function(event) {
var handleObj = event.handleObj,
targetData = jQuery.data(event.target),
now = new Date().getTime(),
delta = targetData.lastTouch ? now - targetData.lastTouch : 0,
delay = delay == null ? 300 : delay;
if (delta < delay && delta > 30) {
targetData.lastTouch = null;
event.type = handleObj.origType;
['clientX', 'clientY', 'pageX', 'pageY'].forEach(function(property) {
event[property] = event.originalEvent.changedTouches[0][property];
})
// let jQuery handle the triggering of "doubletap" event handlers
handleObj.handler.apply(this, arguments);
} else {
targetData.lastTouch = now;
}
}
};
})(jQuery);
Load jQuery Mobile into your project and try using taphold or some of the other mobile specific touch events that are available to you through that API.
Here's the jQuery Mobile documentation with all the events you can use: http://api.jquerymobile.com/category/events/
Here is the snippet for TS React users. Pass in the click event, so that double click is only invoked if the same element is clicked twice
import React from "react";
type CallBack = () => any;
type TapParams = { onSingleTap?: CallBack; onDoubleTap?: CallBack };
var DELTA_TIME_THRESHOLD_MS = 700;
var timer: NodeJS.Timeout | null = null;
var target: EventTarget;
export function tap(
e: React.MouseEvent,
{ onSingleTap, onDoubleTap }: TapParams
) {
if (timer == null) {
// First tap
onSingleTap?.();
timer = setTimeout(() => {
timer = null;
}, DELTA_TIME_THRESHOLD_MS);
} else {
// Second tap
if (e.target === target) {
onDoubleTap?.();
}
clearTimeout(timer);
timer = null;
}
target = e.target;
}
Usage
<div
onClick={(e) => tap(e, { onSingleTap, onDoubleTap })}
>Tap or doubletap</div>
Using only JavaScript
You can use "touchstart" event for a single touch,
but with calculating the time when he should click again
I used 400 (0.4s) as it's the longer duration between two touches
It's only an estimate, but it's still a reasonable time
let expired
let doubleClick = function () {
console.log('double click')
}
let doubleTouch = function (e) {
if (e.touches.length === 1) {
if (!expired) {
expired = e.timeStamp + 400
} else if (e.timeStamp <= expired) {
// remove the default of this event ( Zoom )
e.preventDefault()
doubleClick()
// then reset the variable for other "double Touches" event
expired = null
} else {
// if the second touch was expired, make it as it's the first
expired = e.timeStamp + 400
}
}
}
let element = document.getElementById('btn')
element.addEventListener('touchstart', doubleTouch)
element.addEventListener('dblclick', doubleClick)
In case of this error :
Unable to preventDefault inside passive event listener due to target being treated as passive.
event.preventDefault( ) not working if element = "document" or "document.body"
So the solution of that, you should have a full page div container :
let element = document.getElementById('container')
element.style.minWidth = '100vw'
element.style.minHeight = '100vh'
document.body.style.margin = '0px'
element.addEventListener('touchstart', elementTouch)
element.addEventListener('dblclick', doubleClick)