Detect mouse cursor type - javascript

I want JavaScript code to detect the mouse cursor type.
For example when the cursor hovers in <textarea> it changes from default to text.
How would I go about detecting this?

You could do this, but its not pretty, and will probably be quite slow depending on how many elements you have on your page.
$('*').mouseenter(function(){
var currentCursor = $(this).css('cursor') ;
//do what you want here, i.e.
console.log( currentCursor );
});

You can detect the cursor type using JavaScript
like
<input id="sample_text" name="one" type="text" value="Sample Text" />
and the JavaScript code should look something like this
$('input[id=sample_text]').click( function() {
alert("test");
var ctl = document.getElementById('sample_text');
var startPos = ctl.selectionStart;
var endPos = ctl.selectionEnd;
alert(startPos + ", " + endPos);
});
you can also look at this Jsfiddle for Js Cursor Detection
the above is the Jquery code written , you can also use the Vanilla JS for that you just need to change it to
<input id="sample_text" name="one" type="text" value="Sample Text" onclick="detect_cursor()" />
and the JavaScript should look something like this
function detect_cursor() {
alert("test");
var ctl = document.getElementById('sample_text');
var startPos = ctl.selectionStart;
var endPos = ctl.selectionEnd;
alert(startPos + ", " + endPos);
};

I have a nice jQuery Extension perfect for this type of thing at this gist:
https://gist.github.com/2206057
To use it just do something like:
$("#eleID").cursor(); // will return current cursor state for that element
$("#eleID").cursor("pointer"); // will change ele cursor to whatever is ""
$("#eleID").cursor("clear"); // will change ele cursor to default
$("#eleID").cursor("ishover"); // will return boolean of if mouse is over element or not
$("#eleID").cursor("position"); // will return cursor's current position in "relation" to element
also
$.cursor("wait") // will change document cursor to whatever is ""
$.cursor($("#eleID"), "wait"); // same as $("#eleID").cursor("wait");
$.cursor("position") // will return current cursor position
should also mention, if you submit multiple elements like $("#eleID1, .elementsWiththisClass") for "position" and "isHover" then it will return an Array containing objects like:
var poses = $("#eleID1, .elementsWiththisClass").cursor("position") // will equal
poses[0] = {
ele: e.fn.e.init[1], // the jquery element
x: XXX, // where XXX is the cursors current position in relation to element
y: XXX
}
poses[1] = { // ...and so on and so forth for each element

I think you can read the cursor css property just like you can set it, but you have to do this from a specific element because AFAIK there's no way to just read the cursor type from the window or document object. Following this logic, to get the current cursor type you would have to find the current element the mouse is over and read its cursor css. However, you'd constantly have to check to see if the cursor changed, which is slow and error prone (As a rule you should almost always try to try to put your code in an event handler to react to something instead of constantly checking if its happened yet and putting your code in that function its more logical, efficient, robust, and clear.)
But the idea of detecting the cursor type still fascinates me, and if anyone knows how I'd love to hear about it. :D
As an alternate solution, rather than reading the cursor type, why don't you just set an event handler for when it enters an element that would change it? This would be a lot less error prone and probably more direct, because I don't think you care so much about the cursor, but if the mouse has entered a specific element.
$("#textbox").mouseover( function() {
//I know the cursor has changed because it is now in a textbox
});

As suggested here, using getComputedStyle worked for me.
const onMouseOver = function (e) {
var cursor = getComputedStyle(e.target).cursor;
console.log({ cursor });
};
document.addEventListener("mouseover", onMouseOver, false);
Although this does not help detect the exact cursor type when cursor is set to auto, we can at least use it when cursor is set to something other than auto.

Related

Preventing textarea scroll behaviour in chrome after newline added

Recently my version of chrome has been doing something strange (74.0.3729.131 on ubuntu 18.04) more and more often. I have a small editor script which has a textarea which displays code. The textarea has a fixed size and a vertical scroll bar. Beyond that nothing fancy.
Usually, when I insert a newline (normal behaviour of textarea), the scroll bar doesn't move. Now for some reason about 80% of the times it scrolls the textarea down till the position of the caret is at the top of the textarea. Strangely if I delete and enter the newline in the same position, it usually does not scroll.
I'm not sure if this is some new issue in Chrome. I usen't have this issue with previous versions with the identical editor.
Here is a codepen which demonstrates the issue, scroll to some line, press enter and the textarea should scroll down. Try this a few times to see the unpredictable behaviour (adding the code just to be able to add the link, as you can see it's just a textarea).
https://codepen.io/anon/pen/rgKqMb
<textarea style="width:90%;height:300px"></textarea>
The only solution that occurs to me to avoid this is to stop the normal behaviour of the enter key and add the newline to the text. Any other ideas/insights very much welcome.
It's almost the end of 2020, Chrome version 86 and this issue still exists? What's more, I am surprised I have not found more information (complaints) on this matter (this post is the only thing I've found which speaks of this issue specifically.) I have observed that this behavior occurs not only in typing, but pasting any text containing a newline. I also observed that if I execute an undo action after this occurs, another random scroll happens, taking me even farther up the page, and nowhere near where the caret is.
I experimented and examined this behavior at much length, and was not able to find any repeatable circumstances which might give a clue as to how to predict when this would occur. It truly just seems "random". Nonetheless, I've had to work around this issue for an NWJS editor app I'm creating (NWJS uses Chrome for UI.)
This is what seems to be working for me:
First all, let me start simple in order to introduce the principle. We attach an "input" listener and "scroll" listener to the textarea. This works because, from my observation anyway, the "input"[1] listener gets fired before the random scroll action occurs.
The scroll listener records each scrolling action and saves it in a global prevScrollPos. It also checks for a global flag scrollCorrection.
The "input" listener sets the scrollCorrection flag everytime text is input into the textarea. Remember, this has happened before the random scroll occurs.
So the next scroll to occur, which may be the nuisance random action, the scroll listener will clear scrollCorrection, then scroll the textarea to the previous scroll position, ie, scrolling it back to where it was before the "random" scroll. But the issue is unpredictable, what if there is no random scroll and the next scroll to occur is intentional? That is not a big deal. It just means that if the user scrolls manually, the first scroll event is basically nullified, but then after that (with scrollCorrection cleared) everything will scroll normally. Since during normal scrolling, events are spit out so rapidly, it is unlikely there will be any noticeable effect.
Here is the code:
let textarea;
let prevScrollPos = 0;
let scrollCorrection = false;
function onScroll(evt) {
if (scrollCorrection) {
// Reset this right off so it doesn't get retriggered by the corrction.
scrollCorrection = false;
textarea.scrollTop = prevScrollPos;
}
prevScrollPos = textarea.scrollTop;
}
function onInput(evt) {
scrollCorrection = true;
}
window.addEventListener("load", () => {
textarea = document.getElementById("example_textarea");
textarea.addEventListener("scroll", onScroll);
textarea.addEventListener("input", onInput);
})
Now let's expand on it:
There is another consideration. What if the typing or pasting action puts the end of the typed or pasted text (and thus the caret) outside the view of the textarea viewport? When normal scrolling is in play, most browsers will scroll the page[2] so the caret will remain in view. However now that we've taken over scrolling action, we'll need to implement that ourselves.
In the psuedo-code below, on input to the textarea, besides setting scrollCorrection, we call a function which will:
determine the xy position of caret relative to textarea viewport
determine if it is scrolled out of view
if so:
determine the amount to scroll to bring it in view
determine if the random scroll has already occurred by testing the state of scrollCorrection
if it hasn't, set flag scrollCorrection2 containing the amount to scroll
if it has, explicitly do the additional scrolling to bring it back into view
Finding the xy position of the caret in a textarea is not a trivial matter and is outside the scope of this answer, but there are plenty of methods to be found in searching the web. Most involve replicating the textarea contents in a non-form element, eg div block, with similar font, font-size, text wrapping etc, then using getBoundingClientRect on the resultant containing block and such. In my situation, I was already doing most of this for my editor, so it wasn't much of an additional expense. But I've included some psuedo-code to show how this can be implemented in the scroll correction mechanism. setCaretCorrection basically does steps 1 - 7 above.
let textarea;
let prevScrollPos = 0;
let scrollCorrection = false;
let caretCorrection = 0;
function onScroll(evt) {
if (scrollCorrection) {
// Reset this right off so it doesn't get retriggered by the correction.
scrollCorrection = false;
textarea.scrollTop = prevScrollPos + caretCorrection;
caretCorrection = 0;
}
prevScrollPos = textarea.scrollTop;
}
function onTextareaInput() {
scrollCorrection = true;
setCaretCorrection();
}
function setCaretCorrection(evt) {
let caretPos = textarea.selectionStart;
let scrollingNeeded;
let amountToScroll;
/* ... Some code to determine xy position of caret relative to
textarea viewport, if it is scrolled out of view, and if
so, how much to scroll to bring it in view. ... */
if (scrollingNeeded) {
if (scrollCorrection) {
// scrollCorrection is true meaning random scroll has not occurred yet,
// so flag the scroll listener to add additional correction. This method
// won't cause a flicker which could happen if we scrollBy() explicitly.
caretCorrection = amountToScroll;
} else {
// Random scroll has already occurred and been corrected, so we are
// forced to do the additional "out of viewport" correction explicitly.
// Note, in my situation I never saw this condition happen.
textarea.scrollBy(0, amountToScroll);
}
}
}
One could go further and use the experimental event, "beforeinput"[3], to optimize this a little bit so fewer unnecessary calls to setCaretCorrection are made. If one examines event.data from "beforeinput" event, in certain cases it will report the data to be input. If it does not, then it outputs null. Unfortunately, when a newline is typed, event.data is null. However it will report newlines if they are pasted. So at least one can see if event.data contains a string, and if the string does not contain newlines, skip the whole correction action. (Also, see [1] below.)
[1] I also don't see any reason you couldn't do in the "beforeinput"[3] listener, what what we're doing in the "input" listener. That may also give more insurance that we set scrollCorrection before the random scroll occurs. Although note that "beforeinput" is experimental.
[2] I suspect it is broken implementation of this feature which is causing this issue.
[3] https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/beforeinput_event (Available on Chrome and all major browsers except Firefox according to this link.)
You can try avoiding the events on the textarea with css and js, then force the scroll to it's current position:
css:
textarea {
overflow:auto;
resize:none;
width:90%;
height:300px;
}
js:
You'll need to insert the first answer from this question at A
function preventMoving(e) {
var key = (e.keyCode ? e.keyCode : e.which);
if(key == 13) {
e.preventDefault();
// A
}
}
Then on your HTML:
<textarea onkeyup="preventMoving(event);"></textarea>

change default cursor style in PIXI.js

I want to change my cursor when triggering a mousedown event. I found a lot of hints how to achieve that - but these have one major issue. To change the defaultCursorStyle property I need to instantiate the InteractionManager prototype.
var renderer = PIXI.autoDetectRenderer(640, 480),
var interactionManager = new PIXI.interaction.InteractionManager(renderer)
interactionManager.defaultCursorStyle = "crosshair" // a native html/css cursor style
That looks fine at first but the problem here is that this InteractionManager seems to batch-register all events applied to PIXI object via the .on(event, callback) event-binding function per instance.
That means that if there would be a second instance of this InteractionManager, all events would be bound twice and therefore triggered twice. I had this exact issue.
So I needed to revert my changes and try to access the default InteractionManager. Somebody in the HTML5GameDev forum told me to access it like this:
renderer.plugins.interaction
knowing that I tried the following:
renderer.plugins.interaction.defaultCursorStyle = "crosshair"
My events now worked properly again. But the cursor change did not happen. However debugging the line told me that the property defaultCursorStyle was successfully set to "crosshair". Now I'm looking for a way to make this change visible.
My question:
Is there a better way to change the cursor style than the mentioned one above? If no, how can I make my default cursor change visible after setting the new style to the default InteractionManager?
There's a setCursorMode method in the docs, guess it's what you need.
const app = new PIXI.Application({ height, width })
app.renderer.plugins.interaction.cursorStyles.default = 'crosshair'
setTimeout(() => {
app.renderer.plugins.interaction.setCursorMode('pointer')
}, 1000)
Whenever cursor leaves the renderer, PIXI resets cursor mode (here's exactly the line). So you might want to set new cursor mode as default each time you change it.
function changeCursorMode (cursorMode) {
app.renderer.plugins.interaction.cursorStyles.default = cursorMode
app.renderer.plugins.interaction.setCursorMode(cursorMode)
}
app.renderer.plugins.interaction.cursorStyles.crosshair = 'crosshair'
changeCursorMode('crosshair')

scroll zoom on two images

I have two images with same dimensions and same positions, but placed in divs with dynamic width depending on user interaction using the jquery beforeAfter plugin.
I would like to enable scroll zooming on these images using wheelzoom, such that zooming on one of these images will zoom the same amount in the same position as the other.
What I am unable to do is this linking of (I suppose) the event handlers along the lines of this:
function onwheel(e){
//adjust image to fit zoom level ...
other_img.onwheel(e);
}
If this is not possible, is it possible to copy the event and change the target image?
I am looking for a solution using either jquery or native Javascript.
Code here (ignore the handle).
EDIT: Any top-level pointers to what should work would also be appreciated
I made a worked example: http://plnkr.co/edit/kH0ec8TVMXUIYlMoBaMq?p=preview
here is the core code:
document.getElementsByClassName("before")[0].addEventListener("wheel", function(event){
if(flag){
flag= false;
return;
};
flag=true;
var newEvent = new WheelEvent("wheel",event);
var elementToTrigger = document.getElementsByClassName("after")[0];
elementToTrigger.dispatchEvent(newEvent);
});
I do something simple, when an event happening("wheel") a trigger the same event to another element and pass as argument the data from the first event to the new event. I use flag variable to deter the custom event trigger again the other event and start an eternal loop.
This is a solution without to edit the source code of plugin. You can do more good solutions if you change the code of wheelzoom.js.
Make the zooming a function that takes enough parameters to handle zooming, and get the values you need from the event handler.
function handleZoom(domNode, zoomDirection, zoomAmount) {
// Do stuff to change the size of `domNode`
}
var img1, img2;
img1 = /* select image node */;
img2 = /* select image node */;
function handleScroll(scrollEvent) {
var direction, amount;
// Use `scrollEvent` to figure out which direction and how far to zoom
handleZoom(img1, direction, amount);
handleZoom(img2, direction, amount);
}
img1.onscroll = handleScroll;
img2.onscroll = handleScroll;

Hashchange history breaks iScroll

I have created the following application using iScroll: http://preview.na-software.co.uk/Demo/FutureLearning4/#/section-0
As the user flicks left and right or clicks the arrows in the bottom corners, the application moves the content sections it updates the history by changing the hash so that the user can move back and forth to other sections and bookmark them etc.
However! If you access a hash like: http://preview.na-software.co.uk/Demo/FutureLearning4/#/section-2 and then navigate a few sections and then use the back buttons two issues happen:
1.) It scrolls to the first screen (even though currentSection is correct, and iScroll has been told the correct section).
2.) If you click the back or forward button multiple times, you stop the animation and cause it to become confused and stick in between two sections.
Looking into the code, and seeing that the correct indexes and elements are being passed to iScroll on hashchange, and console logging out the offsets, I've discovered the issue is cause because the offsets are incorrectly set... however just doing refresh() won't fix the issue, as it will then reset the position.
Can anyone see where the problem is or see a way to fix this?
I should note that this bug ONLY happens if you come into the application on a URL that isn't section 0 and then scroll around the application. This is because the offsets will be created correctly by your interactions. But if you come into a URL like section 3, then the offsets will be incorrect and so the hashchanges don't work correctly, if that makes sense.
The hashchange method looks like:
// handle hashchange events
$(window).hashchange( function(){
// read the hash to find out what the new section number is
var nums = location.href.match(/(section)-\d+/g).map(
function(x){ return +x.replace(/\D/g,"") }
);
// set currentSection
currentSection = nums[0];
// if the hashchange was called by user scrolling
if(hashCalledByScroll){
// no need to anything as they have already updated hash and scrolled
hashCalledByScroll = false;
} else {
// find the section to scrollTo
sectionToScrollTo = $('#horizontal > .sections > .section').eq(currentSection).attr('id');
// tell iscroll to scroll to the section
horizontal.scrollToElement( '#' + sectionToScrollTo, null, null, true );
}
// hide the menu on hashchange
hideMenu();
});
Testing your site, I noticed the following: Whenever I access the site via section-3 and then enter the url for section-2, the navigation would instead send me to section-0.
I believe this is the same behaviour as you are experiencing in 1).
So I investigated and came to the following analysis:
In the function horizontal.scrollToElement( '#' + sectionToScrollTo, null, null, true )
iScroll retrieves the utils.offset(el) [iScroll.js#772] for the given el-ement. This offset tells it, where the element to scroll to is.
iScroll goes through the element and all of its offsetParents to add up their offsets. This is where things are breaking: <div class="sections"> has a negative offset to its parent, which imho it should not have.
This, in turn, messes up the scrollTo-coordinates.
To see what I am talking about: document.querySelector('.sections').offsetLeft
This has all just been analysis. My approach to fix this would be to avoid scrollToElement() and instead use scrollTo():
...
} else {
// find the section to scrollTo
sectionToScrollTo = $('#horizontal > .sections > .section').eq(currentSection).attr('id');
// tell iscroll to scroll to the section
var posLeft = -$('#' + sectionToScrollTo)[0].offsetLeft;
var posTop = -$('#' + sectionToScrollTo)[0].offsetTop;
horizontal.scrollTo(posLeft, posTop, 1000);
}
// hide the menu on hashchange
hideMenu();
});
Thus, just calculate the location of the section you want to go to yourself.
About 2) I am not sure if there is much one can do about it. Jumping around quickly breaks a lot of carousels. Maybe a delayed callback to scrollEnd, verifying the validity of the current state.
Another thing I noticed is that you can accidentally stop the transition. Try to click, hold and release the cursor midway a transition - you need to be quick.
Hope this helps.
Found not best solution and it doesn't solve main problem, but it works.
$(window).hashchange(function () {
if (hashCalledByScroll) {
hashCalledByScroll = false;
} else {
var hpage = window.location.hash;
var hpage = hpage.replace('#/section-', ''); //get number of target page
var cpage = currentSection; //number of current page
var count = parseInt(hpage) - parseInt(cpage); //difference
while (count > 0) { //if difference positive: go forward count-times
horizontal.next();
count--;
}
while (count < 0) { //if difference negative: go backward count-times
horizontal.prev();
count++;
}
}
hideMenu();
});
FIDDLE

JavaScript programatically hover mouse over element

I'm writing a vb.net program to automate and manage an online game. I'm using the Awesomium webcontrols to display and manipulate the pages of the game.
There is a point where I need to grab the data that's not shown in the source until the user hovers over a certain element, how can I use javascript (Not jquery please) to hover over it programatically until the data I need becomes available and then grabbed?
I apologise if this has been asked before (Which it has but from the perspective of someone who owns the web page) but I have been searching for hours for a solution and cant find anything.
What I've tried to use but failed is:
function findBpDate(){
document.getElementById('tileDetails').children[1].children[0].children[1].children[0].fireEvent('onmouseover');
return document.getElementsByClassName('text elementText')[0].textContent;
}
This returns "undefined" when it calls back to my application, I'm certain I'm pointing to the right DOM elements though.
This is what I want the javascript to "hover" on:
<span class="a arrow disabled">Send troops</span>
Once this element has been "hovered" on, this elements text changes to the text I need to grab:
<div class="text elementText">Beginners protection until 20/07/13 07:51 am.</div>
I've shown above what the element looks like when the mouse "hovers" on the element I need it to, however this changes a lot depending on which element the user hovers over while playing the game, from what i gather it's where the source keeps the text for each tooltip in the game.
So I need a function that will hover over a certain element and then while it's hovering, grab the text from the tooltip text/"text elementText" element.
Try WebView.InjectMouseMove(x, y).
Something like
public Point GetElementPosition(dynamic element)
{
dynamic rect = element.getBoundingClientRect();
using (rect)
{
return new Point(rect.left, rect.top);
}
}
dynamic element = webView.ExecuteJavascriptWithResult("document.getElementById('id')");
Point pos = GetElementPosition(element);
webView.InjectMouseMove(pos.X, pos.Y);
this is 10x easier with js/dom. http://jsfiddle.net/pA2Vd/
Do this...assuming you can get reference to elements somehow using by Id would have been lot easier.
var elm = document.getElementsByClassName('a arrow disabled')[0];
var txt = document.getElementsByClassName('text elementText')[0];
var evt = new Event('mouseover');
elm.dispatchEvent(evt);
var status = txt.innerText;
(helpfuL stuff down) otherwise you need to capture event, detect who fired it, check if that has this class and tag name. Lot of processing.
var txt,spn,status='';
document.getElementByTagName('span').forEach(function(d){
if (d.tagName=="div" && d.className == 'text elementText'){
var txt = d;
}
}
window.onmouseover = function(e) {
var elm = e.target;
if (elm.tagName=="SPAN" && elm.className == 'a arrow disabled') {
status=txt.innerText;
}
}

Categories