jQuery Blur() not working with click() function - javascript

I am trying to make a tooltip with content disappear when I click outside it, but tried solutions to different posts and none of those solutions work for me.
I think the problem is how I am triggering the tooltip, but is the only way I know, here is the code I have in my js and my html markup.
The tooltip fires well, but it doesn't work with the blur event in any way I tried.
$(document).ready(function() {
function tooltover(target_item){
$(target_item + '> a').click(function(){
$('.tiptover_box').focus();
var pos = $(this).position();
var width = $(this).outerWidth();
var boxw = $(this).next().outerWidth();
var boxh = $(this).next().outerHeight();
$(this).next().css('left', (pos.left - ((boxw/2)-(width/2))));
$(this).next().css('top', (pos.top - (boxh+5)));
$(this).next().addClass('tiptover_show');
$(this).next().delay(5).queue(function(){
$(this).addClass('tt-in');
$(this).dequeue();
});
});
$('.tiptover_box').blur( function(){
$('.tiptover_box').removeClass('tiptover_show tt-in');
});
} tooltover('.tiptover');
});
And HTML
<div class="tiptover">
<a>Link Test</a>
<div class="tiptover_box" style="background-image: url(content/prod_0002.jpg);">
<div>Description.</div>
</div>
</div>

A different approach would be to not focus on the div itself, but focus on the rest of the DOM.
In this solution given by prc322 on a similar question he used the mouseup event on the document itself:
$(document).mouseup(function (e)
{
var container = $(".tiptover_box");
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) // ... nor a descendant of the container
{
container.removeClass('tiptover_show tt-in');
}
});
(edited by me for relevance)
Now your tooltip will be "closed" whenever a mouseup event fired on any element that is not the .tiptover_box or any of its descendants.

Instead of trying to trigger the blur event of a div (which is not as reliable as blurring an input field), you can do what you say you want - when anything is clicked, you hide the tooltip:
$('body').click(function(e){
var tooltipContainer = $('.tiptover_box');
var clickTarget = e.target;
if(!tooltipContainer.is(clickTarget)
&& tooltipContainer.has(clickTarget).length === 0){
tooltipContainer.removeClass('tiptover_show tt-in');
}
});
edit: added test for container and its children, basically same as the answer from Bob.

Related

How do I dispatch a mousedown/click event on the window in JavaScript? [duplicate]

I want a function that tells me which element the mouse cursor is over.
So, for example, if the user's mouse is over this textarea (with id wmd-input), calling window.which_element_is_the_mouse_on() will be functionally equivalent to $("#wmd-input").
DEMO
There's a really cool function called document.elementFromPoint which does what it sounds like.
What we need is to find the x and y coords of the mouse and then call it using those values:
document.addEventListener('mousemove', e => {
console.clear()
console.log( document.elementFromPoint(e.clientX, e.clientY) )
}, {passive: true})
[class^='level']{
width: 100px;
height: 100px;
padding: 15px;
background: #00000033;
}
<div class='level-1'>
<div class='level-2'>
<div class='level-3'>
Hover
</div>
</div>
</div>
document.elementFromPoint
jQuery event object
In newer browsers, you could do the following:
document.querySelectorAll( ":hover" );
That'll give you a NodeList of items that the mouse is currently over in document order. The last element in the NodeList is the most specific, each preceding one should be a parent, grandparent, and so on.
Although the following may not actually answering the question, since this is the first result of googling (the googler may not asking exactly the same question:), hope it will provide some extra input.
There are actually two different approaches to get a list of all elements the mouse is currently over (for newer browsers, perhaps):
The "structural" approach - Ascending DOM tree
As in dherman's answer, one can call
var elements = document.querySelectorAll(':hover');
However, this assumes that only children will overlay their ancestors, which is usually the case, but not true in general, especially when dealing with SVG where element in different branches of the DOM tree may overlap each other.
The "visual" approach - Based on "visual" overlapping
This method uses document.elementFromPoint(x, y) to find the topmost element, temporarily hide it (since we recover it immediately in the same context, the browser will not actually renders this), then go on to find the second topmost element... Looks a little hacky, but it returns what you expect when there are, e.g., siblings elements in a tree occluding each other. Please find this post for more details,
function allElementsFromPoint(x, y) {
var element, elements = [];
var old_visibility = [];
while (true) {
element = document.elementFromPoint(x, y);
if (!element || element === document.documentElement) {
break;
}
elements.push(element);
old_visibility.push(element.style.visibility);
element.style.visibility = 'hidden'; // Temporarily hide the element (without changing the layout)
}
for (var k = 0; k < elements.length; k++) {
elements[k].style.visibility = old_visibility[k];
}
elements.reverse();
return elements;
}
Try both, and check their different returns.
elementFromPoint() gets only the first element in DOM tree. This is mostly not enough for developers needs. To get more than one element at e.g. the current mouse pointer position, this is the function you need:
document.elementsFromPoint(x, y) . // Mind the 's' in elements
This returns an array of all element objects under the given point.
Just pass the mouse X and Y values to this function.
More information is here: DocumentOrShadowRoot.elementsFromPoint()
For very old browsers which are not supported, you may use this answer as a fallback.
The following code will help you to get the element of the mouse pointer. The resulted elements will display in the console.
document.addEventListener('mousemove', function(e) {
console.log(document.elementFromPoint(e.pageX, e.pageY));
})
Mouseover events bubble, so you can put a single listener on the body and wait for them to bubble up, then grab the event.target or event.srcElement:
function getTarget(event) {
var el = event.target || event.srcElement;
return el.nodeType == 1? el : el.parentNode;
}
<body onmouseover="doSomething(getTarget(event));">
You can look at the target of the mouseover event on some suitable ancestor:
var currentElement = null;
document.addEventListener('mouseover', function (e) {
currentElement = e.target;
});
Here’s a demo.
Demo :D
Move your mouse in the snippet window :D
<script>
document.addEventListener('mouseover', function (e) {
console.log ("You are in ", e.target.tagName);
});
</script>
<!-- One simple solution to your problem could be like this: -->
<div>
<input type="text" id="fname" onmousemove="javascript: alert(this.id);" />
<!-- OR -->
<input type="text" id="fname" onclick="javascript: alert(this.id);" />
</div>
<!-- Both mousemove over the field & click on the field displays "fname"-->
<!-- Works fantastic in IE, FireFox, Chrome, Opera. -->
<!-- I didn't test it for Safari. -->
You can use this selector to undermouse object and then manipulate it as a jQuery object:
$(':hover').last();
2022 Update:
document.elementsFromPoint() (Note the 's' in elements) is compatible with all major browsers. It basically does the same thing that elementFrompoint does, but retrieves all the elements in DOM order.
Mozilla has a good example of this:
HTML
<div>
<p>Some text</p>
</div>
<p>Elements at point 30, 20:</p>
<div id="output"></div>
JavaScript
let output = document.getElementById("output");
if (document.elementsFromPoint) {
let elements = document.elementsFromPoint(30, 20);
for (var i = 0; i < elements.length; i++) {
output.textContent += elements[i].localName;
if (i < elements.length - 1) {
output.textContent += " < ";
}
}
} else {
output.innerHTML = "<span style=\"color: red;\">" +
"Browser does not support <code>document.elementsFromPoint()</code>" +
"</span>";
}
Output
Some text
Elements at point 30, 20:
p < div < body < html
https://developer.mozilla.org/en-US/docs/Web/API/Document/elementsFromPoint
The target of the mousemove DOM event is the top-most DOM element under the cursor when the mouse moves:
(function(){
//Don't fire multiple times in a row for the same element
var prevTarget=null;
document.addEventListener('mousemove', function(e) {
//This will be the top-most DOM element under cursor
var target=e.target;
if(target!==prevTarget){
console.log(target);
prevTarget=target;
}
});
})();
This is similar to #Philip Walton's solution, but doesn't require jQuery or a setInterval.
Here's a solution for those that may still be struggling. You want to add a mouseover event on the 'parent' element of the child element(s) you want detected. The below code shows you how to go about it.
const wrapper = document.getElementById('wrapper') //parent element
const position = document.getElementById("displaySelection")
wrapper.addEventListener('mousemove', function(e) {
let elementPointed = document.elementFromPoint(e.clientX, e.clientY)
console.log(elementPointed)
});
Demo on CodePen
Let me start out by saying that I don't recommend using the method I'm about to suggest. It's much better to use event driven development and bind events only to the elements you're interested in knowing whether or not the mouse is over with mouseover, mouseout, mouseenter, mouseleave, etc.
If you absolutely must have the ability to know which element the mouse is over, you'd need to write a function that binds the mouseover event to everything in the DOM, and then store whatever the current element is in some variable.
You could so something like this:
window.which_element_is_the_mouse_on = (function() {
var currentElement;
$("body *").on('mouseover', function(e) {
if(e.target === e.currentTarget) {
currentElement = this;
}
});
return function() {
console.log(currentElement);
}
}());
Basically, I've created an immediate function which sets the event on all elements and stores the current element within the closure to minimize your footprint.
Here's a working demo that calls window.which_element_is_the_mouse_on every second and logs what element the mouse is currently over to the console.
http://jsfiddle.net/LWFpJ/1/

Cancel :active element styling

I have DOM elements with :active CSS styling. If a user makes a click, but never releases the click, I want to be able to cancel the :active styling through Javascript.
I have tried doing document.activeElement.blur() but that doesn't work when the user does not release the click. (See fiddle here.)
How can I force blur an element if the user doesn't release their click?
#bobdye's example doesn't work because <div> elements aren't "focusable" by default.
You can force this behaviour by assigning a tabindex property to the div, here is a fiddle.
HTML
<div class="defocus">.::.:.:.::.</div>
<div class="defocus">:..:.:.:..:</div>
<div class="defocus">.::.:.:.::.</div>
You add the class="defocus" attribute to any element that needs to blur after x seconds.
CSS (relevant)
div:active {
color:lightcoral;
}
JavaScript
(function () {
window.addEventListener("load", function () {
var seconds = 0.15 * 1000;
var defocused = document.getElementsByClassName("defocus");
for (var i = 0, l = defocused.length; i < l; i++) {
var el = defocused[i];
el.style.outline = 0; //optional
el.setAttribute("tabindex", -1);
el.addEventListener("mousedown", blur);
}
function blur(e) {
var el = e.target;
setTimeout(function (el) {
el.blur();
}, seconds, el);
}
});
})();
First we wrap this function in a seaf just as a commodity (it will prevent the blur function and variables from being accessible).
Then we get all the elements with a defocus class.
Then we iterate over them.
First we eliminate the focus outline some browsers use because it looks ugly in a div, but it's up to you.
Then we set a tabindex="-1". Using -1 as an index prevents it from acting as a tab break point but allows it to recieve focus and blur events.
Finally we add the blur() function to the mousedown event which will defocus de element after x seconds.
Then we define the blur() function which will take care of defocusing the element with a setTimeout().
That's it, hope it helps!
Note: I don't particularly care for the bounty, keep your rep!
Note: Thanks to #Adam for pointing out that seaf's variables need the var prefix to prevent them from being global.
This Fiddle a simple example of canceling the active state if the user holds the mouse down for more than 500ms.
It uses a link:
<a id="testlink" href="#">Click this</a>
styled to be red if active, and this Javascript:
var lnk = document.getElementById('testlink');
var mousedown = false;
var timerId = null;
lnk.addEventListener('mousedown', function(e) {
mousedown = true;
timerId = window.setTimeout(function() {
if (mousedown) {
lnk.blur();
}
}, 500);
});
lnk.addEventListener('click', function(e) {
mousedown = false;
window.clearTimeout(timerId);
});
Obviously not customized for your particular case, but a "proof of concept".
to be added to other answers, you may use a transition (delayed or not):http://codepen.io/anon/pen/LEXZGB
*:active {
background: red;
filter:blur(5px);
transition: filter 3s 1s;
}
<script src='http://s.codepen.io/assets/libs/prefixfree.min.js'></script>
see me blured if you click too long.

Clicking with element on other element

I know it sounds silly, but what I want to do is trigger click with some html element hovering over another element.
Lets say we got .cursor that is hovering anchor text. In this case click on .cursor should open a google page.
<div class="cursor"></div>
<div class="htmlPage">
Google
Faccebook
Stack
</div>
Any ideas how to do that?
and this don't count
$('.cursor').click(function(){
$('.htmlPage a').click();
})
Cursor should be movable and should be able to click on other links.
Cursor is that blue circle hovering Google button.
Here I have cursor on google, now on click this should link to google, If i were to click on stack then stack should have opened.
If you are not using IE you can use pointer-events:none in CSS. Then your element will be unresponsive to any mouse interaction (and acting like a ghost foreground element).
The workaround for IE is someting like that:
var x = event.pageX;
var y = event.pageY;
$('.cursor').hide();
var here = document.elementFromPoint(x, y);
$('.cursor').show();
// Do what you want with the element here
// Find the parent a element needed with here.parentNode and here.tagName === "A"
// And then fire the click function
I've never use jQuery but I think it should work.
Hope it could help
you can try to get the ".cursor" position on click and compare to each ".htmlPage a" positions and change the window.location.href with the one of the element that overlaps
$(".cursor").click(function(){
var cursor=$(this);
var cl = cursor.offset().left;
var cr = cl+cursor.width();
var ct = cursor.offset().top;
var cb = ct+cursor.height();
$(".htmlPage a").each(function(){
var page=$(this);
var pl = page.offset().left;
var pr = pl+page.width();
var pt = page.offset().top;
var pb = pt+page.height();
if(((cl>pl&&cl<pr)||(cr>pl&&cr<pr))&&((ct>pt&&ct<pb)||(cb>pt&&cb<pb))){
window.location.href=page.attr("href");
}
});
}).draggable();
http://jsfiddle.net/EUmeB/
$('.cursor').click(function(){
$('.htmlPage a').click();
})
Attach an event handler to the cursor class.
$('.cursor').on('click',function()
{
window.location.href = $(this).siblings('.htmlPage').attr('href');
}
This gets the sibling of the element and makes the location equal to that sibling
To be a little more explicit, this might be best.
$('.cursor').click(function(){
$(this).next().children('a').click();
});
Try this:
Demo
// Target link in the next div, following div.cursor
$("div.cursor").click(function() {
var link = $(this).next(".htmlPage").children("a").attr("href");
window.location.href = link;
});

Erratic mouseover behavior with nested items inside mouseover layer

So let's say we have:
A container for everything
A baseDiv inside that container
//let's create a base layer
var container = document.getElementById('container')
var baseDiv = document.createElement('div')
baseDiv.id = 'baseDiv'
baseDiv.innerText = 'this is the base div'
baseDiv.addEventListener('mouseover', createLayer)
container.appendChild(baseDiv)
When the user mouses over:
A layerOnTop, of the same size is put on top of the baseDiv.
When the user mouses out:
The layerOnTop is removed.
function createLayer(){
console.log('creating layer')
layerOnTop = document.createElement('div')
layerOnTop.id = 'layerOnTop'
layerOnTop.addEventListener('mouseout',
function(){
console.log('removing layer')
return layerOnTop.parentElement.removeChild(layerOnTop)
})
container.appendChild(layerOnTop) }
Simple and works great.
However, when layerOnTop contains elements as well (buttons, inputs), the behavior gets very erratic and starts flicking as you're technically exiting the layerOnTop.
//it contains two textareas
layerOnTop.appendChild(document.createElement('textarea'))
layerOnTop.appendChild(document.createElement('textarea'))
I wish I could use mouseenter but it doesn't seem to be supported by Chrome.
Here's my jsfiddle: http://jsfiddle.net/DjRBP/
How can I stop this? I wish I could merge the textareas and layerOnTop into one large mouseover-handling conglomerate.
You need to check in your mouse out event that it's actually leaving the element. Change your mouseout function to:
function(event) {
var e = event.toElement || event.relatedTarget;
if (e.parentNode == this || e == this) {
// We're not actually leaving the parent node so don't remove layer
return;
}
console.log('removing layer')
return layerOnTop.parentElement.removeChild(layerOnTop)
})

JQuery: Using :not(.active) selector, and adding an Active class, to the item selected

I'm new to Javascript and am having a bit of an issue with using a NOT selector, and adding a class during the function, hopefully this will make sense to someone.
I am creating a small gallery, and my goal is to have clickable navigation, however the active image will redirect to another page when clicked.
Code is as follows:
$("ul#mainGallery li:not(.active) a").click(function(){
var thisListClass = $(this).parent().attr('class');
var activeListId = $(this).parent().attr('id');
var newMarginLeft = (activeListId-3) * -200;
var animateAction = {};
animateAction['margin-left'] = newMarginLeft + 'px';
$("ul#mainGallery").animate(animateAction, 1000);
$('li.active img').animate({width:'100px', height:'100px'},1000)
$(this + 'img').animate({width:'300px', height:'300px'},1000)
$(li.active).removeClass('active');
$(this).parent().addClass('active');
return false;
I know there is likely a much better way to do this, but I can't get my head around it.
Edit: I should probably say what the problem is...
When an active image is clicked, it follows the hyperlink all is well.
When a non active image is clicked, it begins the animation, then (i assume) when the 'active' class is added, instead of returning false, it returns true and follows the hyperlink.
You are binding the click event to $("ul#mainGallery li:not(.active) a") whenever that code is run (presumably on document load). The items which are not active at that point will have that item bound, and changing the class afterwards on other items won't bind this event to them. You will need to either change how you bind it or check inside the function whether the item has that class.
Something like this:
$("ul#mainGallery li a").click(function(){
if(!$(this).parent().hasClass('active')){
var thisListClass = $(this).parent().attr('class');
var activeListId = $(this).parent().attr('id');
var newMarginLeft = (activeListId-3) * -200;
var animateAction = {};
animateAction['margin-left'] = newMarginLeft + 'px';
$("ul#mainGallery").animate(animateAction, 1000);
$('li.active img').animate({width:'100px', height:'100px'},1000)
$(this + 'img').animate({width:'300px', height:'300px'},1000)
$('li.active').removeClass('active');
$(this).parent().addClass('active');
return false;
}
EDIT, or if you prefer to continue using the same selector with the :not and everything, then switch your click function to .live()
To stop the default behaviour use the preventDefault() function
$("ul#mainGallery li:not(.active) a").click(function(e){
e.preventDefault(); // will stop the default behaviour
}
Read more on Jquery docs

Categories

Resources