Changing the style of a leaflet cluster when pressing it - css

I have created a map with clusters created like so:
//create clustering markers
var markers = L.markerClusterGroup({
spiderfyOnMaxZoom: false,
showCoverageOnHover: false,
zoomToBoundsOnClick: false,
singleMarkerMode: true, //makes sure that single incidents looks the same as clusters (but are still treated as single markers)
iconCreateFunction: defineClusterIcon
});
var layer_group = L.geoJSON(geoJson);
markers.addLayer(layer_group);
map.addLayer(markers);
map.fitBounds(markers.getBounds());
In the defineClusterIcon function, I create a SVG which then is converted to HTML and defines the icon:
return L.divIcon({
iconSize: new L.Point(40, 45),
html: html,
classname: 'leaflet-div-icon'
});
I now want to be able to change the style of the cluster (or marker, which also is styled as a cluster), when pressing it - and I want it to return to the original styling when pressed again.
Instead of changing the style of the actual svg elements, I am thinking that it might be easier to just change the style of the class:
.leaflet-div-icon {
background: rgba(0,0,0,0);
border: none;
}
Where I then want to have a border when the cluster/marker has been pressed. I do not know, whether it is possible to change the class within the on clusterclick or click functions, or if it can be done in another way.
My code, as it is now can be found here - where the wanted effect also can be seen on the controls on the right side: http://bl.ocks.org/skov94/f006cd45d2daa2bc67e4f514774fdd0d

Instead of switching the outline property of the leaflet-interactive div, i would toggle a class as you did with the controls on the right side (say a outlined class).
This class toggling has to be done in a "onclick" event handler. Leaflet clustering provide its own cluster click events (clusterclick).
The possible targets of the clusterclick event seem to be either the text, circle, or svg nodes of the cluster. We want to get the enclosing div with class leaflet-interactive to add or remove the outlined class on it. This will be made easily possible with Element.closest:
Javascript file
markers
[...]
.on('clusterclick',function(c) {
console.log("pressed");
map.closePopup();
c.originalEvent.target.closest(".leaflet-interactive")
.classList.toggle("outlined");
});
Then, simply change the style of its circle descendants with css:
CSS file
.leaflet-interactive.outlined circle {
stroke-width: 2px;
stroke: blue;
}
Edit: If you're not familiar with css, the selector means: circle nodes that are descendants of nodes with classes leaflet-interactive AND outlined.

Related

Is there a way to prevent a Shape for being transformed?

That is the thing, i'm creating a shape that covers all canvas to act like a white background, i have a transformer and im listening to canvas clicks, inside it im adding objects into transformer nodes, but i don't like to transform the white shape that im using as a background.
Canvas white Shape
Just don't add transformer into it. I see several solutions:
Set listening = false on that background shape. In that case, it will not trigger any mouse/touch/pointer events
Or set a special name for it in just ignore in click callback
const background = new Konva.Rect({
fill: 'white',
width: stage.width(),
height: stage.height(),
name: 'nonSelectable'
});
stage.on('click', (e) => {
// ignore such shape
if (e.target.hasName('nonSelectable')) {
return;
}
// else attach transformer
});

How do I make the area outside of round button not clickable in javafx

When I make a round Button like this
Button b = new Button();
b.setShape(new Circle(20.0));
b.setText("BUTTON");
b.setStyle("-fx-background-radius: 50em; " +
"-fx-min-width: 50px; " +
"-fx-min-height: 50px; ");
It can be still clicked in area outside of circle like the one I marked red here:
Is there a way to fix this?
Button has the pickOnBounds property, Defines how the picking computation is done for this node when triggered by a MouseEvent or a contains function call.
You need to set pickOnBounds property to false
pickOnBounds doesn't seem settable from CSS, meaning you'll need to set it from code
b.pickOnBoundsProperty().setValue(false);

Add css class to leaflet layer during runtime

I'm using leaflet to draw layers of circles (so there are several layers, each consisting of several circles) on a map.
I've saved all the layers on a featuregroup:
this.globalLayer = L.featureGroup();
I'm adding new circles to it by creating a new featuregroup of the circles, and adding the featuregroup to the globalLayer:
let circleLayer: L.featureGroup();
let point1 = L.circle([pos_lat, pos_long], {color: color, opacity: 1,
radius: radius});
let point2 = L.circle([pos_lat, pos_long], {color: color, opacity: 1,
radius: radius});
circleLayer.addLayer(point1);
circleLayer.addLayer(point2);
// etc.
this.globalLayer.addLayer(circleLayer);
Now I want to add a css class to some of the layers:
for (let cssLayer of cssLayers) { // cssLayers is a L.featureGroup[]
this.globalLayer.removeLayer(cssLayer);
cssLayer.setStyle({className: 'animate'});
this.globalLayer.addLayer(cssLayer);
}
This works, but since the layers contain a lot of circles, this takes a while to compute. Is there a way to just add a css Class without removing and adding them again?
I've tried
this.globalLayer.eachLayer(layer => {
layer.setStyle({className: 'animate'})
});
But setStyle() does not exist on type L.Layer
JsFiddle with my current, workaround solution
You will need to add a class to the corresponding layer before you add it to other labels, like
circleLayer1.setStyle({className: 'myListener'});
and then you can find this class anytime you want:
$('#blink').click(function() {
$(".myListener").addClass("blink");
});
Fiddle.
I'm not sure if this is the best practice but I found that you can use the layer's _path attribute:
this.globalLayer.eachLayer(layer => {
layer._path.classList.add('animate')
});

Pulling a style from a TinyMCE selection

I'm trying to implement a TinyMCE button that will apply the style of the selection to the entire box. I'm having trouble, though, reading the style of the selection when the selection is buried in a span in a span in a paragraph. Let's consider 'color' for example. Below I have a box with some text and I've selected "here" in the paragraph and made it red.
The HTML for the paragraph is now:
The code behind my button to apply the style of the selection to the box is
var selected_color = $(ed.selection.getNode()).css('color');
console.log("color pulled is ", selected_color);
$(ed.bodyElement).css('color', selected_color);
It doesn't work because the color pulled is black, not red, so the third line just re-applies the black that's already there. (If I replace selected_color in the third line with 'blue' everything goes blue.) So the problem is pulling the color of the current selection.
Does anyone know how I can do this reliably, no matter how buried the selection is?
Thanks for any help.
I also noticed somewhat a strange behavior up and there, with selections of nested span's and div's, but honestly i'm not able to recognize if this is a bug of TinyMCE, a browser issue or a combination of both (most probably).
So, waiting for some more information from you (maybe also your plugin code) in the meanwhile i realized two proposal to achieve what you want: the first plugin behaves like the format painter in word, the second is simply applying the current detected foreground color to the whole paragraph.
As you move throug the editor with the keyboard or mouse, you will see the current detected foreground color highlighted and applied as background to the second plugin button.
Key point here are two functions to get the styles back from the cursor position:
function findStyle(el, attr) {
var styles, style, color;
try {
styles = $(el).attr('style');
if(typeof styles !== typeof undefined && styles !== false) {
styles.split(";").forEach(function(e) {
style = e.split(":");
if($.trim(style[0]) === attr) {
color = $(el).css(attr);
}
});
}
} catch (err) {}
return color;
}
function findForeColor(node) {
var $el = $(node), color;
while ($el.prop("tagName").toUpperCase() != "BODY") {
color = findStyle($el, "color");
if (color) break;
$el = $el.parent();
}
return color;
}
The try...catch block is needed to avoid some occasional errors when a selected text is restyled. If you look at the TinyMCE sorce code you will notice a plenty of timing events, this is a unavoidable and common practice when dealing with styles and css, even more with user interaction. There was a great job done by the authors of TinyMCE to make the editor cross-browser.
You can try out the first plugin in the Fiddle below. The second plugin is simpler as the first one. lastForeColor is determined in ed.on('NodeChange'), so the code in button click is very easy.
tinymce.PluginManager.add('example2', function(ed, url) {
// Add a button that opens a window
ed.addButton('example2', {
text: '',
icon: "apply-forecolor",
onclick: function() {
if(lastForeColor) {
var applyColor = lastForeColor;
ed.execCommand('SelectAll');
ed.fire('SelectionChange');
ed.execCommand('forecolor', false, applyColor);
ed.selection.collapse(false);
ed.fire('SelectionChange');
}
return false;
}
});
});
Moreover: i think there is a potential issue with your piece of code here:
$(ed.bodyElement).css('color', selected_color);
i guess the style should be applied in a different way, so in my example i'm using standard TinyMCE commands to apply the foreground color to all, as i wasn't able to exactly convert your screenshot to code. Please share your thoughts in a comment.
Fiddle with both plugins: https://jsfiddle.net/ufp0Lvow/
deblocker,
Amazing work! Thank you!
Your jsfiddle did the trick. I replaced the HTML with what was in my example and changed the selector in tinymce.init from a textarea to a div and it pulls the color out perfectly from my example. The modified jsfiddle is at https://jsfiddle.net/79r3vkyq/3/ . I'll be studying and learning from your code for a long time.
Regarding your question about
$(ed.bodyElement).css('color', selected_color);
the divs I attach tinymce to all have ids and the one the editor is currently attached to is reported in ed.bodyElement. I haven't had any trouble using this but I have no problem using your
ed.execCommand('SelectAll');
ed.fire('SelectionChange');
ed.execCommand('forecolor', false, applyColor);
Thanks again! Great job!

How to change styling of a SVGCircleElement object?

In my scatterplot I'd like to change the styling(opacity/color) of a circle on mouseover and also for all other circles which, share the same className.
But groupList[i].style("opacity", .6); seems not to be the correct way.
var mouseOver = function() {
var circle = d3.select(this);
var highlitedGroup = this.className.baseVal;
var groupList = document.getElementsByClassName(highlitedGroup);
// The styling for the circle which mouse is over it.
circle.transition()
.duration(800).style("opacity", 1)
.attr("r", 16).ease("elastic");
// For the all other circles which have the same className do this styling
for (var i=0; i<groupList.length; i++) {
// List of SVGCircleElement objects
groupList[i].style("opacity", .6); //??
}
}
Because you are already putting D3.js to use, I recommend sticking to it throughout your code whenever possible. In this case your function boils down to basically two statement where the first one is manipulating the main circle while second one will take care of all circles having the same class.
var mouseOver = function() {
// The styling for the circle which mouse is over it.
d3.select(this).transition()
.duration(800).style("opacity", 1)
.attr("r", 16).ease("elastic");
// For the all other circles which have the same className do this styling
d3.selectAll("." + this.className)
.style("opacity", .6);
}
Please note, that this will only work if there is only one class assigned to the main circle. If there is more than one class assigned to this element, this.className will contain a space-separated list of class names breaking the selection.

Resources