d3.js: click equivalent of mouseout - javascript

What I mean by a click equivalent of mouseout is that I'd like a way to click on an element to change some attribute then have it change back when I click on anything but that element. Toggling this attribute change with hovering is easy because you change things based on mouseover and mouseout, but I'm unsure of how to do the same based on the click event.
So what I have is an svg element with circles on it, which display a red outline when they're clicked on. I know enough to be able to make only one circle appear selected at a time, but I don't know how to deselect all nodes when I click on a part of the svg that isn't a circle. If this isn't clear enough, I can create a jsfiddle to demonstrate what I have so far.
I have a working knowledge of selections from reading many examples, but can't seem to figure out what approach I should take to achieve this.

You could use d3.dispatch to set up some custom event handling. Sometimes separating out distinct behaviors from the rest of your layout code helps to keep things organized.
You might want one function to unhighlight all of the clickable circles, and another to toggle a single circle. Then when the svg is clicked, you can decide whether to unhighlight all based on whether a circle was clicked or not.
In other words...
When a circle is clicked, toggle it.
When the svg document is clicked, and the click is not on a circle, unhighlight all circles.
Then you can set up separate dispatch events for the two processes. This is nice because then these become reusable behaviors. If for example, you later want to add a button to unhighlight all circles, or want to highlight a circle when it's moused over, you can call the same dispatch functions.
var dispatch = d3.dispatch('unhighlightAll','toggleSingle')
// remove the `highlighted` class on all circles
.on('unhighlightAll', function() {
d3.selectAll('.clickable-circle').classed('highlighted', false);
})
// toggle the `highlighted` class on element `el`
.on('toggleSingle', function(el) {
d3.select(el).classed('highlighted', function() {
return !d3.select(el).classed('highlighted');
});
});
Finally, you call the dispatch functions from your click handlers:
svg.on('click', function() {
// do nothing if a clickable circle is clicked
if (d3.select(d3.event.target).classed('clickable-circle')) {
return;
} else {
// otherwise unhighlight all circles
dispatch.unhighlightAll();
}
});
circles.on('click', function() {
dispatch.toggleSingle(this);
});
Then all that's left is to decide how to display the highlighted class, and handle that in your css.
Here's a demo JSBin
--EDIT--
I just realized that since you're trying to mimic mouseout, you probably don't want multi-select. You'd just need to change the toggleSingle function a bit:
dispatch.on('toggleSingle', function(el) {
// store state of current element
var highlighted = d3.select(el).classed('highlighted');
// unhighlight all
dispatch.unhighlightAll();
// set opposite of stored state
d3.select(el).classed('highlighted', !highlighted);
});
And here's the updated JSBin.

Add a click handler on the SVG. In that click handler, first deselect all circles. Then, check the event target via d3.event; if it is a circle, select it. Pseudocode-ish description:
svg.on('click', function() {
circles.classed('selected', false);
var target = /* get event target */;
if (/* target is circle */) {
target.classed('selected', true);
}
});

Jshanley's answer is great and is probably better in most cases, but I ended up modifying it a bit:
svg.selectAll("dot").data(datasource).enter() //you probably have your own thing here
.on("mousedown", function(d) {
d3.selectAll("circle") //this selects all of the elements you want to deselect by html tag (here its "circle")
.style("fill", "black"); //default color of unselected elements, here its black
d3.select(this) //select the element that's just been clicked
.style("fill", "orange"); //orange is the color of currently selected element
});
This works as long as the default style on all elements is applied before the style of the selected element.

Related

Pixi js - need to make clickable background

I need to be able to click on background, and get the position of the click.
I tried adding an event listener to stage like this
app.stage.interactive = true;
app.stage.on('click', function(){
console.log('hello');
})
but it works only if i click on element inside the stage, not the background itself.
Do i need to make a sprite as a background, if so, how do i set its background color and make sure it stays under all the other elements?
The stage is a PIXI.Container, which means it's basically an empty node that can hold children. It doesn't have dimensions of its own, and so when the interaction manager goes to hit-test your click, it isn't detected.
Your suggestion of adding a background sprite is probably the simplest solution. You can add one like so:
// Create the background sprite with a basic white texture
let bg = new PIXI.Sprite(PIXI.Texture.WHITE);
// Set it to fill the screen
bg.width = app.screen.width;
bg.height = app.screen.height;
// Tint it to whatever color you want, here red
bg.tint = 0xff0000;
// Add a click handler
bg.interactive = true;
bg.on('click', function(){
console.log('hello');
});
// Add it to the stage as the first object
app.stage.addChild(bg);
// Now add anything else you want on your stage
...
PixiJS renders objects in order, so if you add your background sprite as the first child of the app's stage, it will be rendered behind all other content. When hit-testing a click, it will be the last object tested, and so will catch a click on the background of the stage. Note that to "block" clicks, other objects will need to be set to interactive = true, even if they don't have a click handler attached!
A possible solution would be to add an event on app's view.
app.renderer.view.addEventListener('click', function(e) {
console.log(e);
});

Get click event from SVG bounding box using d3js

I am trying to add a mouse click event listener to click events on the bounding box of an SVG element (in this case all elements of the class "measure").
What I thought should work is this:
var vrvToolkit = new verovio.toolkit(); // www.verovio.org
jQuery.ajax({
url: "http://www.verovio.org/examples/downloads/Schubert_Lindenbaum.mei",
success: function (data) {
var svg = vrvToolkit.renderData(data + "\n", "");
jQuery("#svg_output").html(svg);
},
async: false
});
d3.select("#svg_output svg").selectAll(".measure")
.on("click", function () {
jQuery("#log").text(d3.mouse(this));
});
see JSFiddle
Unfortunately only mouse clicks on actually drawn elements are registered, the white space is ignored.
Is there a way to recognize mouse clicks inside a specific bounding box?
You cannot detect clicks on none existing elements in d3.
If you want to detect your bounding box, you need to create a new element with the same shape (for example: <polygon>), make it invisible and add it to the group you want to listen to.
Usually people create a big transparent rectangle.
All you need to do is add the click handler on the entire SVG and not specific elements:
d3.select("#svg_output svg")
.on("click", function () {
jQuery("#log").text(d3.mouse(this));
});
Complete demo here. To get the element that is being clicked on, you can use d3.event.target (https://jsfiddle.net/78hLhugh/3/).

jQuery-UI tooltip shows after second mouseover

I have this piece of code:
(...)
if (data.hasSentMessages === true){
$("#sentMessages")
.parent()
.removeClass("ui-state-disabled");
} else {
$("#sentMessages")
.parent()
.addClass("ui-state-disabled");
}
And then, if element has disabled class, I want to show why is element disabled in jQuery-UI Tooltip. But unfortunately it shows after second mouseover event.
When I read this: Tooltip not showing on first mouseover I create something like this:
$("#sentMessages").tooltip({disabled: !($("#sentMessages").parent().hasClass("ui-state-disabled"))});
if($("#sentMessages").data("tooltip") === false){
$("#sentMessages").tooltip({disabled: !($("#sentMessages").parent().hasClass("ui-state-disabled"))}).triggerHandler("mouseover");
}
This condition, if widget is not initialized, should manually triggered mouseover event but unfortunately still it isn't working.
I will be very happy if somebody wants to help me - thank you in advance.
There is a simplified Fiddle: jsFiddle that shows a problem, there you can see that second tooltip is good but first is like ,,standard" browser tooltip.
In your fiddle, the initialisation of the tooltip happens within the mouseover callback function. This will mean that the tooltip is not created until after the first mouseover event.
Secondarily the mouseover event is built into the way that the jQueryUI tooltip functions. As such, you can remove the mouseover code.
Thirdly, you had mentioned that the tooltip should only display when the parent element has the class ui-state-disabled.
See the result on JSFiddle.
// construct a tooltip for each sentMessages then listen for the
// tooltipopen event an evaluate if it should show the tooltip
// or not
$("#sentMessages").tooltip().on('tooltipopen', function(){
if ($(this).parent().hasClass('ui-state-disabled')){
$(this).tooltip("enable");
} else {
$(this).tooltip("disabled");
}
});
// comment out this line to replicate a non error state
$("#sentMessages").parent().addClass("ui-state-disabled");
I hope this helps.
based on your example fiddle: add e.stopImmediatePropagation() to the mouseover event, see updated fiddle:

jQuery: Click on *only* this element

In the highly-artistic drawing above, the green square is a child of the pink one. The pink one is wrapped around the green one by my function, so the green square could be anything - a hyperlink, an image, button, etc.
I want to capture a click on the pink div ONLY if it isn't a click on the green element too.
This could be done by flipping a Boolean using mouseenter on the green square, but that seems a messy way to do it to me.
Any clues?
IMPORTANT EDIT: I can't mess with the green square at all, so no adding anything to the click event.
You can do this:
$('.pink-box-selector').click(function(e) {
if ($(e.target).is('.pink-box-selector')) {
// do stuff here
}
});
Two options. You can first either check if the target is the green div.
$('#pinkdiv').click(function(e) {
if ($(e.target).is('#greendiv')) return;
// handle the event
});
Or you can write the click handler for the pink div normally and stop clicks on the green div from propagating.
$('#greendiv').click(function(e) {
e.stopPropagation();
});
Would $("#div_id :not('#excluded_element')).click(); help?
http://api.jquery.com/not-selector/
Setup a separate click event listener for the "green" element and have it event.preventDefault()
http://jsfiddle.net/rlemon/d8qVp/
$("#pink").click(function(){
if(!$("#green").is(":hover")) {
alert('here');
}
});

Javascript onClick and onmouseover and onmouseOut

I would like to ask how to differentiate between onClick and onMouseOver and onMouseOut.
For instance,
I use onMouseOver to change the tab background to grey using
onMouseOver="this.style.backgroundColor=Blue;"
onMouseOut takes away this background
onMouseOut="this.style.backgroundColor=White;"
How do I write a call for onClick that gives a blue background and keeps it there even when the mouse cursor moves away from the tab?
Thanks
How about you have two CSS classes, "active" and "clicked". They both set the background to blue. On click, you add the "clicked" class. On mouse over, you add the "active" class. On mouse out, you remove the "active" class. If the element had the "clicked" class it'd still have it, and hence keep its color.
You need to work with event listeners. I recommend using a framework for this, like prototypejs or jquery.
You will need event listeners / observers for observing the mouseOver, mouseOut and click events. When your click event fires, you stop observing for mouseOver and mouseOut. This should do the trick.
I would do a check in the MouseOut that only changes the color if it's not already blue.
Normally I would add a CSS class to the element which has the active (last clicked on item) state. That CSS class can have priority over the normal styling of the element.
I would even use CSS and not JavaScript for hover state changes.
Very simple solution is here for your help. Hope it works for you.
<script type="text/javascript">
function row_mout(mout_value) {
document.getElementById(mout_value).style.background = "#FFFFFF";
}
function row_mover(mover_value){
document.getElementById(mover_value).style.background = "#123456";
}
function row_click(click_value) {
if(document.getElementById('chk_'+click_value).checked == true){
document.getElementById(click_value).style.background = "#123456";
document.getElementById(click_value).onmouseout = "";
document.getElementById(click_value).onmouseover = "";
}//if over
else if(document.getElementById('chk_'+click_value).checked == false){
document.getElementById(click_value).style.background = "#FFFFFF";
document.getElementById(click_value).onmouseout = function onmouseout(event){
row_mout(click_value);
}
document.getElementById(click_value).onmouseover = function onmouseover(event){
row_mover(click_value);
}
}//else if over
}

Categories

Resources