It only increase or decrease when the click is pressed and moved from top to bottom, That is normal operation.
I have this script that allows dragging from the Input increment and decrement value, this works well in Firefox but in Chrome does not work correctly, can anyone correct this bug (in Chrome)?
I have problems even with the script. It only works in firefox, but it does not work the same in Chrome.
Example: http://jsfiddle.net/iLen/bacx5qem/
Video: http://recordit.co/yW1NUPrXBP (left: chrome / right: firefox)
var clicking = false;
$('.vamoAlSubibaja').mousedown(function() {
clicking = true;
});
$(document).mouseup(function() {
clicking = false;
})
var i = 0;
var y = 0;
$('.vamoAlSubibaja').mousemove(function(my) {
if (clicking == false) {
return
} else {
// change value
if (my.pageY <= $(this).offset().top + $('.vamoAlSubibaja').css('width').replace('px', '') / 10) {
y = parseInt($(this).val()) + 1;
$('.movestatus').text('plus');
} else {
y = parseInt($(this).val()) - 1;
$('.movestatus').text('minus');
}
$(this).val(parseInt(y));
i++;
}
});
It's not working because you are assigning the mousemove event to the div, not to the document.
If you want each div to respond independently you need to assign a value to a variable to know which was clicked down. This way when the mouse moves (after clicking down one of the divs) you can assign your function to that specific div.
This demo is only fixing the mousemove so you can see how to solve your main issue.
var clicking = false;
$('.vamoAlSubibaja').mousedown(function() {
clicking = true;
});
$(document).mouseup(function() {
clicking = false;
})
var i = 0;
var y = 0;
$(document).mousemove(function(my) {
if (clicking == false) {
return
} else {
// change value
if (my.pageY <= $('.vamoAlSubibaja').offset().top + $('.vamoAlSubibaja').css('width').replace('px', '') / 10) {
y = parseInt($('.vamoAlSubibaja').val()) + 1;
$('.movestatus').text('plus');
} else {
y = parseInt($('.vamoAlSubibaja').val()) - 1;
$('.movestatus').text('minus');
}
$('.vamoAlSubibaja').val(parseInt(y));
i++;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<i>Only when the mouse moves up and down does
the value change from the input
(validate min y max)</i>
<br />
<br />
<br />
<br />
<br />
<input type="text" value="5" class="vamoAlSubibaja" id="change_value_up_down" />
<input type="text" value="5" class="vamoAlSubibaja" id="change_value_up_down2" />
<!-- only ref -->
<span class="clickstatus"></span>
<span class="movestatus"></span>
As per the official DOM Level 3 documentation
In some implementation environments, such as a browser, mousemove events can continue to fire if the user began a drag operation (e.g., a mouse button is pressed) and the pointing device has left the boundary of the user agent.
In this case Firefox continues to fire the mousemove event when the mouse leave the input limits, and Chrome doesn't. That's why you should follow the solution by #Alvaro as a reference and use the document instead the input.
Keep in mind as well that
Implementations are encouraged to determine the optimal frequency rate to balance responsiveness with performance.
So the effect wont look exactly the same across browsers.
Related
I'm new here so maybe my question will be a bit difficult to understand.
So here's the problem:
I have built a website for myself in html, JavaScript, and css which includes a music player.
It's not ready yet cause I have many problems with it. But let's focus on the main problem.
So like every other music player application, mine also has a time slider. The main function would be to display the current song's current time and to make me able to skip it wherever I want. I guess you know what I mean despite my english level.
The skip part is working just fine. Not perfect but okay for now.
But the slider's movement is weird. It starts okay in the first few seconds, but after it beginst to take more and more time to jump to the next point.
This is the skipping part's code:
let slider = document.getElementById("durationSlide");
function changeDuration(){
//console.log("currentTime: " + currentSong.currentTime);
sliderPosition = currentSong.duration * (slider.value / 100);
currentSong.currentTime = sliderPosition;
}
And this would be the part where I was supposed to just follow the little dot along the slide 'till the end, but it stops after a while:
function rangeSlider(){
let position = 0;
if(!isNaN(currentSong.duration)){
position = currentSong.currentTime * (100 / currentSong.duration);
slider.value = position;
}
if(currentSong.ended){
currentSong.currentTime = 0;
currentCounter += 1;
position = 0;
PlayFunction();
}
}
UPDATE:
my code is pretty messy...
The function where it is called in every second. (I hope)
function PlayFunction() {
timer = setInterval(rangeSlider, 1000);
playIcon.style.display = "none";
pauseIcon.style.display = "inline";
songList[currentCounter].classList.add("current");
//console.log(songList[currentCounter].classList);
titles.innerHTML = songList[currentCounter].title;
songCounter.innerHTML = currentCounter+1 + "/" + document.getElementsByTagName("audio").length;
checkCurrent();
}
And the html for the slider:
<div class="duration">
<input class="durSlider" id="durationSlide" type="range" min="0"
max="100" value="0"
onchange="changeDuration()">
</div>
testimg
from 0 to 10-13 seconds it goes fine but after it starts to skip time.
And every time I click on pause it jumps to the position where it supposed to be.
Your code appears to work fine as I could not duplicate the problem. The lag you see is probably related to how your code interfaces with the audio player and the browser UI. If you are using a timer loop to update the UI, then one change that might help is switching to event based updates. As shown in the code snippet below, the audio player has a number of events you could use to update the position and duration. For example, this event handler would allow your audio player to update the UI with the position changes. If you haven't read it already, MDN has a very nice walkthrough on using the Web Audio API
currentSong.addEventListener("timeupdate", function(e) {
if (!busy) {
position.value = currentSong.currentTime;
positionText.innerHTML = toTimeString(currentSong.currentTime);
}
});
// Original Code
let slider = document.getElementById("durationSlide");
function changeDuration() {
sliderPosition = currentSong.duration * (slider.value / 100);
currentSong.currentTime = sliderPosition;
}
// And this would be the part where I was supposed to just follow the little dot along the slide 'till the end, but it stops after a while:
function rangeSlider() {
let position = 0;
if (!isNaN(currentSong.duration)) {
position = currentSong.currentTime * (100 / currentSong.duration);
slider.value = position;
}
if (currentSong.ended) {
currentSong.currentTime = 0;
currentCounter += 1;
position = 0;
PlayFunction();
}
}
// Code used for testing
var busy = false;
// fired when media properties like duration are known
currentSong.addEventListener("loadedmetadata", function() {
position.max = currentSong.duration;
});
// do not update value when user is trying to change it
currentSong.addEventListener("timeupdate", function(e) {
if (!busy) {
position.value = currentSong.currentTime;
positionText.innerHTML = toTimeString(currentSong.currentTime);
}
});
currentSong.addEventListener("ended", function() {
position.value = 0;
});
// user is setting position
position.addEventListener("input", function() {
busy = true;
positionText.innerHTML = toTimeString(position.value);
});
play.addEventListener("click", function() {
if (currentSong.paused) {
currentSong.play();
play.classList.add("active");
} else {
currentSong.pause();
play.classList.remove("active");
}
});
position.addEventListener("change", function() {
busy = false;
currentSong.currentTime = position.value;
})
// volume 0.0 to 1.0
volume.addEventListener("input", function() {
currentSong.volume = volume.value;
volumeText.innerHTML = (100 * volume.value).toFixed() + "%";
});
function toTimeString(sec) {
return new Date(sec * 1000).toISOString().slice(11, -5);
}
#play:after {
font-weight: bold;
font-size: 1.2em;
content: ">";
/* play symbol */
}
#play.active:after {
content: "II";
/* pause symbol */
}
<h3>Demo</h3>
<p>Click the play button and use the position and volume sliders.</p>
<audio id="currentSong" src="https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3"></audio>
<div style="padding:2px;margin:5px;">
<button id="play"></button>
</div>
<div>
<div>Position:</div>
<input type="range" value="0" id="position" />
<span id="positionText"></span>
</div>
<div>
<div>Volume:</div>
<input type="range" min="0" max="1" step="0.01" id="volume" />
<span id="volumeText"></span>
</div>
I have an inelegant workaround for this issue, and am hoping that others may already have more robust solutions.
On a touchscreen, tapping on an editable text field will bring up an on-screen keyboard, and this will change the amount of screen space available. Left untreated, this may hide key elements, or push a footer out of place.
On a laptop or desktop computer, opening an editable text field creates no such layout changes.
In my current project, I want to ensure that certain key items are visible even when a virtual keyboard is open, so I need to detect when such a change occurs. I can then add a class to the body element, to change the layout to suit the presence of the keyboard.
When searching for existing solutions online, I discovered that:
There is no perfect way of knowing that your code is running on a mobile device
There are non-mobile devices that have touchscreens, and which may also have keyboards
A focus element may not be editable
contentEditable elements will open the on-screen keyboard
The address bar may decide to reappear and take up essential screen space at the same time the virtual keyboard appears, squeezing the available space even more.
I have posted the solution that I have come up with below. It relies on detecting a change in height of the window within a second of the keyboard focus changing. I am hoping that you might have a better solution to propose that has been tested cross-platform, cross-browser and across devices.
I've created a repository on GitHub.
You can test my solution here.
In my tests, this may give a false positive if the user is using a computer with a touchscreen and a keyboard and mouse, and uses the mouse first to (de-)select an editable element and then immediately changes the window height. If you find other false positives or negatives, either on a computer or a mobile device, please let me know.
;(function (){
class Keyboard {
constructor () {
this.screenWidth = screen.width // detect orientation
this.windowHeight = window.innerHeight // detect keyboard change
this.listeners = {
resize: []
, keyboardchange: []
, focuschange: []
}
this.isTouchScreen = 'ontouchstart' in document.documentElement
this.focusElement = null
this.changeFocusTime = new Date().getTime()
this.focusDelay = 1000 // at least 600 ms is required
let focuschange = this.focuschange.bind(this)
document.addEventListener("focus", focuschange, true)
document.addEventListener("blur", focuschange, true)
window.onresize = this.resizeWindow.bind(this)
}
focuschange(event) {
let target = event.target
let elementType = null
let checkType = false
let checkEnabled = false
let checkEditable = true
if (event.type === "focus") {
elementType = target.nodeName
this.focusElement = target
switch (elementType) {
case "INPUT":
checkType = true
case "TEXTAREA":
checkEditable = false
checkEnabled = true
break
}
if (checkType) {
let type = target.type
switch (type) {
case "color":
case "checkbox":
case "radio":
case "date":
case "file":
case "month":
case "time":
this.focusElement = null
checkEnabled = false
default:
elementType += "[type=" + type +"]"
}
}
if (checkEnabled) {
if (target.disabled) {
elementType += " (disabled)"
this.focusElement = null
}
}
if (checkEditable) {
if (!target.contentEditable) {
elementType = null
this.focusElement = null
}
}
} else {
this.focusElement = null
}
this.changeFocusTime = new Date().getTime()
this.listeners.focuschange.forEach(listener => {
listener(this.focusElement, elementType)
})
}
resizeWindow() {
let screenWidth = screen.width;
let windowHeight = window.innerHeight
let dimensions = {
width: innerWidth
, height: windowHeight
}
let orientation = (screenWidth > screen.height)
? "landscape"
: "portrait"
let focusAge = new Date().getTime() - this.changeFocusTime
let closed = !this.focusElement
&& (focusAge < this.focusDelay)
&& (this.windowHeight < windowHeight)
let opened = this.focusElement
&& (focusAge < this.focusDelay)
&& (this.windowHeight > windowHeight)
if ((this.screenWidth === screenWidth) && this.isTouchScreen) {
// No change of orientation
// opened or closed can only be true if height has changed.
//
// Edge case
// * Will give a false positive for keyboard change.
// * The user has a tablet computer with both screen and
// keyboard, and has just clicked into or out of an
// editable area, and also changed the window height in
// the appropriate direction, all with the mouse.
if (opened) {
this.keyboardchange("shown", dimensions)
} else if (closed) {
this.keyboardchange("hidden", dimensions)
} else {
// Assume this is a desktop touchscreen computer with
// resizable windows
this.resize(dimensions, orientation)
}
} else {
// Orientation has changed
this.resize(dimensions, orientation)
}
this.windowHeight = windowHeight
this.screenWidth = screenWidth
}
keyboardchange(change, dimensions) {
this.listeners.keyboardchange.forEach(listener => {
listener(change, dimensions)
})
}
resize(dimensions, orientation) {
this.listeners.resize.forEach(listener => {
listener(dimensions, orientation)
})
}
addEventListener(eventName, listener) {
// log("*addEventListener " + eventName)
let listeners = this.listeners[eventName] || []
if (listeners.indexOf(listener) < 0) {
listeners.push(listener)
}
}
removeEventListener(eventName, listener) {
let listeners = this.listeners[eventName] || []
let index = listeners.indexOf(listener)
if (index < 0) {
} else {
listeners.slice(index, 1)
}
}
}
window.keyboard = new Keyboard()
})()
There is a new experimental API that is meant exactly to track size changes due to the keyboard appearing and other mobile weirdness like that.
window.visualViewport
https://developer.mozilla.org/en-US/docs/Web/API/Visual_Viewport_API
By listening to resize events and comparing the height to the height to the so called "layout viewport". See that it changed by a significant amount, like maybe 30 pixels. You might deduce something like "the keyboard is showing".
if('visualViewport' in window) {
window.visualViewport.addEventListener('resize', function(event) {
if(event.target.height + 30 < document.scrollElement.clientHeight) {
console.log("keyboard up?");
} else {
console.log("keyboard down?");
}
});
}
(code above is untested and I suspect zooming might trigger false positive, might have to check for scaling changes as well)
As no direct way to detect the keyboard opening, you can only detect by the height and width. See more
In javascript screen.availHeight and screen.availWidth maybe help.
Using visualViewPort
This was inspired by on-screen-keyboard-detector. It works on Android and iOS.
if ('visualViewport' in window) {
const VIEWPORT_VS_CLIENT_HEIGHT_RATIO = 0.75;
window.visualViewport.addEventListener('resize', function (event) {
if (
(event.target.height * event.target.scale) / window.screen.height <
VIEWPORT_VS_CLIENT_HEIGHT_RATIO
)
console.log('keyboard is shown');
else console.log('keyboard is hidden');
});
}
Another approach using virtualKeyboard
This worked, but isn't supported in iOS yet.
if ('virtualKeyboard' in navigator) {
// Tell the browser you are taking care of virtual keyboard occlusions yourself.
navigator.virtualKeyboard.overlaysContent = true;
navigator.virtualKeyboard.addEventListener('geometrychange', (event) => {
const { x, y, width, height } = event.target.boundingRect;
if (height > 0) console.log('keyboard is shown');
else console.log('keyboard is hidden');
});
Source: https://developer.chrome.com/docs/web-platform/virtual-keyboard/
This is a difficult problem to get 'right'. You can try and hide the footer on input element focus, and show on blur, but that isn't always reliable on iOS. Every so often (one time in ten, say, on my iPhone 4S) the focus event seems to fail to fire (or maybe there is a race condition with JQuery Mobile), and the footer does not get hidden.
After much trial and error, I came up with this interesting solution:
<head>
...various JS and CSS imports...
<script type="text/javascript">
document.write( '<style>#footer{visibility:hidden}#media(min-height:' + ($( window ).height() - 10) + 'px){#footer{visibility:visible}}</style>' );
</script>
</head>
Essentially: use JavaScript to determine the window height of the device, then dynamically create a CSS media query to hide the footer when the height of the window shrinks by 10 pixels. Because opening the keyboard resizes the browser display, this never fails on iOS. Because it's using the CSS engine rather than JavaScript, it's much faster and smoother too!
Note: I found using 'visibility:hidden' less glitchy than 'display:none' or 'position:static', but your mileage may vary.
I'm detecting the visibility of a virtual keyboard as follows:
window.addEventListener('resize', (event) => {
// if current/available height ratio is small enough, virtual keyboard is probably visible
const isKeyboardHidden = ((window.innerHeight / window.screen.availHeight) > 0.6);
});
I am in a fix. I am not able to identify a way to capture the keyboard show/hide status on a mobile device browser.
Problem :
I have a popup on a form in which a Text Field is present. When the user taps on the text field the keyboard shows up pushing the popup on the form and eventually making the text field invisible.
Is there a way to identify the key board show/hide status???
No, there is no way to reliably know when a keyboard is showing. The one level of control you do have is you can set your app to pan or resize when the keyboard shows up. If you set it to resize, it will recalculate your layout and shrink things so if fits the remaining screen. If you choose pan, it will keep the same size and just slide up the entire app.
you can find out keyboard show/hide inside your application,Try following code inside oncreate method,and pass your parent layout to view.
final View activityRootView = rellayLoginParent;
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
Rect r = new Rect();
// r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
//MyLog.w("height difference is", "" + heightDiff);
if (heightDiff > 100)
{ // if more than 100 pixels, its probably a keyboard...
if(lytAppHeader.getVisibility() == View.VISIBLE)
{
lytAppHeader.setVisibility(View.GONE);
}
}
else
{
if(lytAppHeader.getVisibility() == View.GONE)
{
lytAppHeader.setVisibility(View.VISIBLE);
}
}
}
});
It seems there is no reliable way to do this in the browser. The closest I have come is to listen for focus events and then temporarily listen for resize events. If a resize occurs in the next < 1 second, it's very likely that the keyboard is up.
Apologies for the jQuery...
onDocumentReady = function() {
var $document = $(document);
var $window = $(window);
var initialHeight = window.outerHeight;
var currentHeight = initialHeight;
// Listen to all future text inputs
// If it's a focus, listen for a resize.
$document.on("focus.keyboard", "input[type='text'],textarea", function(event) {
// If there is a resize immediately after, we assume the keyboard is in.
$window.on("resize.keyboard", function() {
$window.off("resize.keyboard");
currentHeight = window.outerHeight;
if (currentHeight < initialHeight) {
window.isKeyboardIn = true;
}
});
// Only listen for half a second.
setTimeout($window.off.bind($window, "resize.keyboard"), 500);
});
// On blur, check whether the screen has returned to normal
$document.on("blur.keyboard", "input[type="text"],textarea", function() {
if (window.isKeyboardIn) {
setTimeout(function() {
currentHeight = window.outerHeight;
if (currentHeight === initialHeight) {
window.isKeyboardIn = false;
}, 500);
}
});
};
I'm trying to drag and drop items on a page, but insteads of moving the item itself, I create a copy of it.
Here is my code. "copyDragDrop" is a div at the bottom of the page. It remains empty until the user strats dragging something.
function coordSouris()
{
return {
x:event.clientX + document.body.scrollLeft - document.body.clientLeft,
y:event.clientY + document.body.scrollTop - document.body.clientTop
};
}
function drag()
{
var pos = coordSouris(event);
copie = event.srcElement.cloneNode(true);
document.getElementById('copieDragDrop').appendChild(copie);
copie.style.position = 'absolute';
copie.style.display = 'block';
document.onmousemove = mouseMove;
document.onmouseup = drop;
}
function mouseMove()
{
if (copie != null)
{
var pos = coordSouris(event);
copie.style.left = pos.x;
copie.style.top = pos.y;
}
}
function drop()
{
var divCopie = document.getElementById('copieDragDrop');
if (divCopie.hasChildNodes() )
{
while ( divCopie.childNodes.length >= 1 )
{
divCopie.removeChild(divCopie.firstChild);
}
}
}
This code creates the copy, starts to move it, but after a few pixels, the copy stops following the mouse. If I release the mouse, "onmouseup" is not fired, but the copy starts to follow the mouse again ! I've tried the code on several items, the same bug occurs
I don't understand anything, any help is more than welcome.
UPDATE : I juste realised that all elements I tried the code on had something in common : they contained or were included in an ASP.net hyperlink control. The same code works well on regular HTML elements. There must be some auto-generated javascript for links that interferes with my code.
Couldn't find the auto-generated code responsible for the issue, so I simply solvec this by replacing Hyperlink controls by standard HTML anchors.
Is it possible to fire a specific JavaScript event when a certain DIV comes into view on the page?
Say, for example, I have a very large page, like 2500x2500 and I have a 40x40 div that sits at position 1980x1250. The div is not necessarily manually positioned, it could be there due to the content pushing it there. Now, is it possible to run a function when the user scrolls to a point where the div becomes visible?
Not automatically. You would have to catch scroll events and check for it being in view each time by comparing the co-ordinates of the div rectangle with the visible page rectangle.
Here's a minimal example.
<div id="importantdiv">hello</div>
<script type="text/javascript">
function VisibilityMonitor(element, showfn, hidefn) {
var isshown= false;
function check() {
if (rectsIntersect(getPageRect(), getElementRect(element)) !== isshown) {
isshown= !isshown;
isshown? showfn() : hidefn();
}
};
window.onscroll=window.onresize= check;
check();
}
function getPageRect() {
var isquirks= document.compatMode!=='BackCompat';
var page= isquirks? document.documentElement : document.body;
var x= page.scrollLeft;
var y= page.scrollTop;
var w= 'innerWidth' in window? window.innerWidth : page.clientWidth;
var h= 'innerHeight' in window? window.innerHeight : page.clientHeight;
return [x, y, x+w, y+h];
}
function getElementRect(element) {
var x= 0, y= 0;
var w= element.offsetWidth, h= element.offsetHeight;
while (element.offsetParent!==null) {
x+= element.offsetLeft;
y+= element.offsetTop;
element= element.offsetParent;
}
return [x, y, x+w, y+h];
}
function rectsIntersect(a, b) {
return a[0]<b[2] && a[2]>b[0] && a[1]<b[3] && a[3]>b[1];
}
VisibilityMonitor(
document.getElementById('importantdiv'),
function() {
alert('div in view!');
},
function() {
alert('div gone away!');
}
);
</script>
You could improve this by:
making it catch onscroll on all ancestors that have overflow scroll or auto and adjusting the top/left co-ords for their scroll positions
detecting overflow scroll, auto and hidden cropping putting the div off-screen
using addEventListener/attachEvent to allow multiple VisibilityMonitors and other things using the resize/scroll events
some compatibility hacks to getElementRect to make the co-ords more accurate in some cases, and some event unbinding to avoid IE6-7 memory leaks, if you really need to.
Here is a solution that is ideal in 2022. The current top answer only allows you to observe one item, and has performance issues because it fires many times every time the page scrolls.
var observer = new IntersectionObserver(function(entries) {
if(entries[0].isIntersecting === true) {
console.log('Item has just APPEARED!');
} else {
console.log('Item has just DISAPPEARED!');
}
}, { threshold: [0] });
observer.observe(document.querySelector("#DIV-TO-OBSERVE"));
This fires as soon as the item is partially on screen. Changing threshold to 1 will require the item to be fully on screen (so it will never fire if the item is bigger than the viewport). You can do values in between for example 0.25 to fire when at least 1/4 of the item is in view.
Here's an starter example using jQuery:
<html>
<head><title>In View</title></head>
<body>
<div style="text-align:center; font-size:larger" id="top"></div>
<fieldset style="text-align:center; font-size:larger" id="middle">
<legend id="msg"></legend>
<div> </div>
<div id="findme">Here I am!!!</div>
</fieldset>
<div style="text-align:center; font-size:larger" id="bottom"></div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
var $findme = $('#findme'),
$msg = $('#msg');
function Scrolled() {
var findmeOffset = $findme.offset(),
findmeTop = findmeOffset.top,
scrollTop = $(document).scrollTop(),
visibleBottom = window.innerHeight;
if (findmeTop < scrollTop + visibleBottom) {
$msg.text('findme is visible');
}
else {
$msg.text('findme is NOT visible');
}
}
function Setup() {
var $top = $('#top'),
$bottom = $('#bottom');
$top.height(500);
$bottom.height(500);
$(window).scroll(function() {
Scrolled();
});
}
$(document).ready(function() {
Setup();
});
</script>
</body>
</html>
It only notifies once the div comes into view from the bottom. This example does not notify when the div scrolls out of the top.