Remove multiple layers on leaflet draw - javascript

I use leaflet draw for drawing polygons on a map.
At the moment, I'm only able to delete the clicked polygons.
This is an example of what I have right now:
http://leaflet.github.io/Leaflet.draw/docs/examples-0.7.x/full.html
Let's say that you draw 3 polygons. If you want to delete the first 2, you have to click on the trash icon, click on the first 2, and click save. What I want to achieve is to not have to click on the second one. I actually have the id of the second but I can't manage to add it to the removingLayers array that keeps the clicked layers in.
What I have atm:
function deleteSubPolygons(e) {
var layersToRemove = [];
if (e.layer && e.layer._originalPoints != null && e.layer._latlngs != null && e.layer.id != null && Number.isInteger(e.layer.id)) {
var polygonChildren = getPoligonChildren(e.layer.id);
for (var l in map._layers) {
if (polygonChildren.indexOf(map._layers[l].id) > -1) {
polygonsToDelete.push(map._layers[l].id);
layersToRemove.push(map._layers[l]);
}
}
for (var i = 0; i < layersToRemove.length; i++) {
map.removeLayer(layersToRemove[i]);
}
}
}
I managed to remove them from the view manually, but the revert option doesn't work well anymore.I'm pretty sure I should just push the layer I want to remove to a list of leaflet-draw.js as they are doing:
this._deletableLayers.removeLayer(e),
this._deletedLayers.addLayer(e)
My question would be: How can I acces the _deletedLayers from outside?

I solved this by firing the click event on the polygons I want to delete when I'm in delete mode:
for (var l in map._layers) {
if (polygonChildren.indexOf(map._layers[l].id) > -1) {
map._layers[l].fireEvent('click');
}
}

Related

JavaScript - For loop within a for loop triggering twice on button press

I have a series of horizontal lists within one vertical list. Each list item has an ID 'cr-id-x_y' where x is the vertical list item index and y is the horizontal list item index. I have created a for loop within a for loop to determine which list item has been targeted. The problem I'm having is the button presses seem to be firing twice (i.e. I'm seeing "Hello world" printed to the console twice on a button press). Please can someone tell me where I'm going wrong?
document.getElementById("parent-list").addEventListener("click", function (e) {
for (let x = 0; x < 100; x++) {
for (let i = 0; i < 30; i++) {
if (
e.target.closest(`#cr-id-${x}_${i}`) &&
(e.target.nodeName == "BUTTON" ||
e.target.parentElement.nodeName == "BUTTON")
) {
console.log("Hello world");
}
}
}
});
From the code you posted, it is not possible to guess why it is firing twice, but actually there is no need to loop at all. When you delegate like you do, you should be able to interrogate the clicked target - for example like this
document.getElementById("parent-list").addEventListener("click", function(e) {
const tgt = e.target; // or if nested e.target.closest('button'), then use tgt in the next if
if (tgt.tagName==="BUTTON") {
const someParent = tgt.closest('someElementThatMightHaveCRId');
if(someParent.id && someParent.id.startsWith('cr-id-')) {
console.log("Hello world",someParent.id);
}
}
})

Modifying Selectize.js library to allow editing of selected options

Using Selectize.js in an Angular 9 application for selecting multiple values. Please see links to my UI at the end
https://selectize.github.io/selectize.js/
https://github.com/selectize/selectize.js
I'm trying enable the user to edit the already selected values by simply clicking on the selected item. Selectize has the concept of Plugins by which "features can be added to Selectize without modifying the main library." I'm making use of this concept to override onMouseDown event, where I'm attempting to make the clicked item editable. I have successfully used this method to override onKeyDown to implement editing of the last selected value by clicking on backspace. Please see code pasted at the bottom. this.onKeyDown = (function() {...
https://github.com/selectize/selectize.js/blob/master/docs/plugins.md
The already selected items are shown as a layer of div elements over the underlying input. To make a selected item editable, I'm removing the selected element div from the DOM, populating the underlying input element with the text from the div. That way that particular item becomes a input from a div and is editable.
There are a few issues im running into:
Its not possible to determine the caret position from the div that was clicked. I am able to get the div text and pre-populate the input element but not put the caret at the right place in input. By default the caret shows at the end and the user can move it around.
Corer cases around when a name is already being edited and the user clicks on another item to edit. The selectize library is giving api to insert selections only at the end of the already selected items. For me to keep deleting the div's and populating the input to mimic the editing effect I need to be able to insert at different positions but the library doesnt seem to have the capability for it.
Trying to see if anyone has worked on something similar or has any suggestions. Thanks in advance!
var Selectize = require('./selectize-standalone');
(function () {
Selectize.define('break_on_backspace_custom_plugin', function(options) {
var self = this;
options.text = options.text || function(option) {
return option[this.settings.labelField];
};
this.onMouseDown = (function() {
var original = self.onMouseDown;
return function(e) {
var index, option;
if (!this.$control_input.val().length && this.$activeItems.length > 0) {
index = this.caretPos - 1;
var toBeEdited = this.$activeItems[0];
var toBeEditedText = toBeEdited.textContent;
var text = toBeEditedText.substring(0, toBeEditedText.length - 1);
var prevEdit = localStorage.getItem("currentEdit");
if (index >= 0 && index < this.items.length) {
if (this.deleteSelection(e)) {
localStorage.setItem("currentEdit", text);
this.setTextboxValue(text);
this.refreshOptions(true);
if (prevEdit && prevEdit !== text) {
this.addItem(prevEdit);
}
}
//e.preventDefault();
//return;
}
}
//e.preventDefault();
return original.apply(this, arguments);
};
})();
this.onKeyDown = (function() {
var original = self.onKeyDown;
return function(e) {
var index, option;
if (e.keyCode === 8 && this.$control_input.val() === '' && !this.$activeItems.length) {
index = this.caretPos - 1;
if (index >= 0 && index < this.items.length) {
option = this.options[this.items[index]];
if (this.deleteSelection(e)) {
//option.value = option.value.substring(0, option.value.length - 1);
this.setTextboxValue(options.text.apply(this, [option]));
this.refreshOptions(true);
}
e.preventDefault();
return;
}
}
return original.apply(this, arguments);
};
})();
});
return Selectize;
})();
Pictures of UI and work in progress
Editing last element by clicking backspace
https://i.stack.imgur.com/wULcT.png
Editing middle element by clicking on it
https://i.stack.imgur.com/U5hxd.png

Minesweeper reveal nearby tiles function

I am trying to make the blank or "0" function of ms in a web page.
The goal:
what happens in mine: what should happen:
the red square indicates the button that was clicked, and the green circles indicate its adjacent squares/tiles.
My approach or logic to make this function was:
step1: if the button that was clicked is a 0, reveal its adjacent tiles.
step 2: for every adjacent tile, if THAT tile is 0, reveal THAT TILES' adjacent tiles. and so on until all adjacent tiles of every connected 0 is revealed.
The code of my function: (the parameters are the coordinates of the button that is clicked. e.g.the red square/tile in the picture above has coordinates 3,6)
function RevealNearbyTiles(y,x){
var cordsx; //cordsx and cordsy represent the adjacent tiles of the coordinates (the parameters).
var cordsy;
var coordinates;
for(i=-1; i<2; i++){ //for every adjacent tile:
for(j=-1;j<2;j++){
cordsx = x;
cordsy = y;
if(i === 0 && j === 0){
continue;
}
else{
cordsx += j;
cordsy += i;
//if this ^ offset is within the grid:
if((cordsx >= 0 && cordsx < 10) && (cordsy >= 0 && cordsy < 10)){
//the coordinates of the tile.
coordinates = $("#mstable tr:nth-of-type("+(cordsy+1)+") td:nth-of-type("+(cordsx+1)+") .tiles");
//if it has not been revealed
if(coordinates.parent().attr("data-revealed") === "false"){
//reveal this coordinate.
coordinates.empty().append("<p id='number'>"+coordinates.parent().attr("data-value")+"</p>");
coordinates.parent().attr("data-revealed", "true");
//if this coordinate is 0
if(coordinates.parent().attr("data-value") === " "){
//reveal this coordiantes' nerabytiles
RevealNearbyTiles(cordsy,cordsx);
}
}
}
}
}
}
}
The attribute "data-value" is the number of nearby bombs the tile has.
The attribute "data-revealed" is true if the tile is revealed, or false if it's not. They both work, don't worry too much about them.
The code for every tile button:
$(".tiles").click(function(){
//if this button is clicked, reveal this tile
$(this).empty().append("<p id='number'>"+$(this).parent().attr("data-value")+"</p>");
$(this).parent().attr("data-revealed","true");
//if this tile's value is 0, call the function
if($(this).parent().attr("data-value") === " "){
RevealNearbyTiles($(this).parent().data("index").a,$(this).parent().data("index").b);
}
});
What I think the problem is: The for loop is supposed to run for every adjacent tile of the tile that was clicked, but when it runs the function for the first tile, it forgets about all other adjacent tiles. I need to make it so that the function runs on all adjacent tiles that are 0, and on all of THEIR adjacent tiles that are 0 and so on.
Thanks for your help, its a hard problem to explain =/. I searched many places but could not find an answer. Sorry for the very long and specific problem.
I think the issue is with your two for loops, they use global vars called i and j which are the same for each call to RevealNearbyTiles(). You can fix this with the following code...
for(var i=-1; i<2; i++){ //for every adjacent tile:
for(var j=-1;j<2;j++){
I think what happened was that your algorithm ran until it hit the case where it had revealed all adjacent tiles (e.g. i = 1, j = 1), it then exited up the call chain with those values and exited each loop rather than keep executing them.

Event listener fails to attach or remove in some contexts

I've created a script that attaches an event listener to a collection of pictures by default. When the elements are clicked, the listener swaps out for another event that changes the image source and pushes the id of the element to an array, and that reverses if you click on the swapped image (the source changes back and the last element in the array is removed). There is a button to "clear" all of the images by setting the default source and resetting the event listener, but it doesn't fire reliably and sometimes fires with a delay, causing only the last element in a series to be collected.
TL;DR: An event fires very unreliably for no discernible reason, and I'd love to know why this is happening and how I should fix it. The JSFiddle and published version are available below.
I've uploaded the current version here, and you can trip the error by selecting multiple tables, pressing "Cancel", and selecting those buttons again. Normally the error starts on the second or third pass.
I've also got a fiddle.
The layout will be a bit wacky on desktops and laptops since it was designed for phone screens, but you'll be able to see the issue and inspect the code so that shouldn't be a problem.
Code blocks:
Unset all the selected tables:
function tableClear() {
//alert(document.getElementsByClassName('eatPlace')[tableResEnum].src);
//numResTables = document.getElementsByClassName('eatPlace').src.length;
tableArrayLength = tableArray.length - 1;
for (tableResEnum = 0; tableResEnum <= tableArrayLength; tableResEnum += 1) {
tableSrces = tableArray[tableResEnum].src;
//alert(tableSrcTapped);
if (tableSrces === tableSrcTapped) {
tableArray[tableResEnum].removeEventListener('click', tableUntap);
tableArray[tableResEnum].addEventListener('click', tableTap);
tableArray[tableResEnum].src = window.location + 'resources/tableBase.svg';
} /*else if () {
}*/
}
resTableArray.splice(0, resTableArray.length);
}
Set/Unset a particular table:
tableUntap = function () {
$(this).unbind('click', tableUntap);
$(this).bind('click', tableTap);
this.setAttribute('src', 'resources/tableBase.svg');
resTableArray.shift(this);
};
tableTap = function () {
$(this).unbind('click', tableTap);
$(this).bind('click', tableUntap);
this.setAttribute('src', 'resources/tableTapped.svg');
resTableArray.push($(this).attr('id'));
};
Convert the elements within the 'eatPlace' class to an array:
$('.eatPlace').bind('click', tableTap);
tableList = document.getElementsByClassName('eatPlace');
tableArray = Array.prototype.slice.call(tableList);
Table instantiation:
for (tableEnum = 1; tableEnum <= tableNum; tableEnum += 1) {
tableImg = document.createElement('IMG');
tableImg.setAttribute('src', 'resources/tableBase.svg');
tableImg.setAttribute('id', 'table' + tableEnum);
tableImg.setAttribute('class', 'eatPlace');
tableImg.setAttribute('width', '15%');
tableImg.setAttribute('height', '15%');
$('#tableBox').append(tableImg, tableEnum);
if (tableEnum % 4 === 0) {
$('#tableBox').append("\n");
}
if (tableEnum === tableNum) {
$('#tableBox').append("<div id='subbles' class='ajaxButton'>Next</div>");
$('#tableBox').append("<div id='cazzles' class='ajaxButton'>Cancel</div>");
}
}
First mistake is in tapping and untapping tables.
When you push a Table to your array, your pushing its ID.
resTableArray.push($(this).attr('id'));
It will add id's of elements, depending on the order of user clicking the tables.
While untapping its always removing the first table.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
resTableArray.shift(this);
So, when user clicks tables 1, 2, 3. And unclicks 3, the shift will remove table 1.
Lets fix this by removing untapped table
tableUntap = function () {
$(this).unbind('click', tableUntap);
$(this).bind('click', tableTap);
this.setAttribute('src', 'http://imgur.com/a7J8OJ5.png');
var elementID = $(this).attr('id');
var elementIndex = resTableArray.indexOf(elementID);
resTableArray.splice(elementIndex, 1);
};
So you were missing some tables after untapping.
Well lets fix tableClear,
You have a array with tapped tables, but you are searching in main array.
function tableClear() {
tableLen = resTableArray.length;
for (var i = 0; i < tableLen; i++) {
var idString = "#" + resTableArray[i];
var $element = $(idString);
$element.unbind('click', tableUntap);
$element.bind('click', tableTap);
$element.attr("src", 'http://imgur.com/a7J8OJ5.png');
}
resTableArray = [];
}
Im searching only tapped tables, and then just untap them and remove handlers.
fiddle: http://jsfiddle.net/r9ewnxzs/
Your mistake was to wrongly remove at untapping elements.

jVectorMap: How do I add a class to the selected region?

I have the below code:
onRegionClick: function (event, code) {
// search for the state based on the code of the region clicked.
for (var r = 0; r < mapData.stateList.length; r++) {
if (mapData.stateList[r].state == code) {
if (mapData.stateList[r].markets.length == 1) {
// state only has one region - navigate to it.
window.location = mapData.stateList[r].markets[0].url;
break;
} else {
// state has multiple regions - zoom into it on the map and show the markets.
$("#map-reset").show();
$('.map-label').text('Click a city below to view communities in that area.');
$('body').addClass('map-zoomed');
showState(code);
break;
}
}
}
}
How would I add a class to the selected region? I have tried several routes based on similar questions found through Google and Stack Overflow to no avail. Any help is greatly appreciated.
Check my way to fix it:
http://pastebin.com/s5GwcEMy
i add this method "setSelectedRegionStyle"
You need get reference to the map:
map = $("#world-map-gdp").vectorMap('get', 'mapObject');
After you can set your custom color:
map.setSelectedRegionStyle('IT', '#b2c9cb');
In my case only need change the color, but you can use the firebug to check the another options.
This is the added method (Check in the pastbin)
setSelectedRegionStyle : function (r,c) {
return this.regions[r].element.style.selected.fill = c;
},

Categories