source clear BUG in OpenLayers 3 - javascript

I'm using OpenLayers 3 (v3.20). What I want to achieve is just to remove all features from a particular layer. I see that there is a clear method and documentation says, that
clear(opt_fast)
Remove all features from the source.
However, when I apply it to my layer source like so:
layer.getSource().clear();
I see a blink (features are removed) and then I see a server request, so that features are reloaded again. So, either documentation is incomplete, or there is a bug.
I also tried to remove features like so:
features = source.getFeatures();
for (i = 0; i < features.length; i += 1) {
source.removeFeature(features[i]);
}
But it works really strange. If, for example, I have four features, when I loop once, it removes just two features and when I loop twice, one extra feature is removed. All in all, I have to loop three times (which is indeed not DRY) to remove all features. I really wonder, why is that and how can I fix it. Thanks!

As pointed by Karl-Johan Sjögren, removing a array member when iterate through it modifies the array itself, so, you use reverse array or use a native function from Array MDN reference:
features = source.getFeatures();
features.forEach(function (feature){
source.removeFeature(feature);
});

Related

Undefined index... but it says it's defined in the debugger

(EDIT: I solved my issue! Though I still don't understand the situation I see in the debugger. See my answer for more details)
(TL;DR: index is always undefined when used with a certain array. Doubt that would be enough info, but maybe for someone who's experienced this before.)
So basically, I'm using an array in javascript, and I started noticing some odd behaviour, so I went to the debugger, and I found that a defined variable representing the index was being treated as undefined. It's ONLY the case with this specific array, and it's index. I don't get errors saying that it's undefined, but when I look in the debugger, it says it's undefined when I hover over the variable in the array call (but it's defined if I hover over it anywhere before the array call), and I'm getting bugs that make it clear that the array is not being used properly. It makes absolutely no sense to me, but maybe someone's encountered a similar issue.
Take this example of code, It's drawing a tilemap layer for my MapRenderer class. The culprit here is "this.Map.layers". When I go into this function in the debugger, layerIndex is defined if I hover over the function parameter, but if I hover over it on the array call, it says it's undefined, and the whole logic breaks.
DrawLayer(ctx, camPos, layerIndex)
{
// Get the map/tile position based on the camera position, to decide which tile to start drawing.
var mapCamPos = new Point(Math.floor(camPos.x/TILESIZE),
Math.floor(camPos.y/TILESIZE));
// Get the max tile position based on camera position, to decide where to stop drawing.
var camPosLimit = new Point(Math.ceil(this.DrawSize.x/TILESIZE)+mapCamPos.x,
Math.ceil(this.DrawSize.y/TILESIZE)+mapCamPos.y);
// loop through all tiles we need to draw using rows and columns.
for(var row=mapCamPos.y;row<this.Map.layers[layerIndex].height&&row<=camPosLimit.y;row++)
{
for(var col=mapCamPos.x;col<this.Map.layers[layerIndex].width&&col<=camPosLimit.x;col++)
{
var currentTileID = this.GetTileID(layerIndex, row, col);
if (currentTileID >= 0 && !isNaN(currentTileID))
{
var drawPos = new Point(((col*TILESIZE)-camPos.x), ((row*TILESIZE)-camPos.y));
this.Spritesheet.PlayFrame(currentTileID);
this.Spritesheet.Draw(ctx, drawPos);
}
}
}
}
This is happening in many instances of my code wherever I'm using that array. I want to add how this started, because all of this logic was working previously. I had my tilemap working with multiple csv files, which I loaded as 2d arrays into an array. Today, I decided to switch it all to use one json file, as it is simply cleaner (one file rather than one csv per map layer), and I can add extra properties and stuff in the future rather than just having the tileIDs. So, in the above example, this.Map gets initialized through an ajax call(using jquery) to read the json file, before DrawLayer ever gets called. Still, I don't see why this would cause this. Doing "mapRenderer.Map.layers" in the console tells me that it's a normal array, and when I try calling it normally from the console, it works fine. I'm so confused at this issue. I had literally the same function before and it worked, just that my array has changed a bit(it used to be just "this.Layers", instead of "this.Map.layers"), but it's still a normal array... I don't see why it would behave so differently just because it was generated via json...
Any help or explanations would be greatly appreciated, thanks.
I still don't understand the situation I see in the debugger, maybe it's a firefox bug, or feature I don't understand. But I managed to fix my issue. It was a basic logic bug: I'm using the "Tiled" map editor, and when you export those maps to CSVs, the tile IDs are zero-based, meaning empty tiles are -1. When you export to json, they aren't zero-based, meaning empty tiles are 0, which I failed to notice, and this was the root of all my current issues. If anyone can explain why the firefox debugger might say defined variables are "undefined" when you hover over them, that would still be good to know.

Restructuring DOM searching method?

As part of a Chrome extension I am searching the entire DOM for elements containing specific words inside each ID/Class.
Currently it looks something like:
"allSelectors": document.querySelectorAll("[id*='example'][class*='example']"),
"collapse": function () {
for (var i = 0; i < MyObject.allSelectors.length; i++) {
// Manipulate MyObject.allSelectors[i] etc
}
},
First, I would like to restructure it somehow (possibly using an array?) so that it is easy to add new selectors as doing it like this:
document.querySelectorAll("[id*='example'][class*='example'][id*='other'][class*='other']")
Isn't easily scaleable or good.
Secondly, I think document.querySelectorAll is very slow - the reason I am using it is because I need to search anywhere in an id/class (hence the use of *=) and cannot use a big external library (such as jQuery), as this is a small file and is being injected user side.
Is there an any solution to either of these problems? because if there are many matches then this slowness might become an issue.
First of all I would totally go for querySelectorAll, I don't think it's that slow, and also it totally fits in a situation like yours. I agree with you that adding a library is overkill for this, and additionally it might not be as beneficial as someone thinks (let's test it here).
Then, again I agree with you that the current solution is not very scalable and that the array is the way to go. Here's a very basic implementation using an array:
// an array of classes and ids to match
var nodes,
searches = [
'[id*="test"]',
'[class*="example"]'
];
// a simple function to return an array of nodes
// that match the content of the array
function getNodes(arr){
return Array.prototype.slice.call(document.querySelectorAll( arr.join() ));
}
nodes = getNodes(searches);
The good thing is that new classes and ids can be easily added or removed from the array, for example, later on you can add:
searches.push('[id*="some"]');
nodes = getNodes(searches); // new nodes will be fetched
Here's a jsbin with a full example code.

touch.identifier value is always 0 for different touch points

I was testing out the example for multi touch in canvas from Mozilla https://developer.mozilla.org/en-US/docs/Web/Guide/Touch_events
Here's the issue I am facing: the identifier attribute for Touch interface ALWAYS shows integer value of 0 on every single touch point on laptop (I am using Google Chrome Version 26.0.1410.64 m), which is a touch ultrabook.
According to the specs, identifier should be unique for each touch but it shows up as 0 for every new touch point on the canvas.
Please help me understand what's causing it because I want to use the identifier unique value to map it to different colors and make a paint on touch canvas like application.
In response to your comment, I take it you're referring to this bit of code in particular:
function ongoingTouchIndexById(idToFind) {
for (var i=0; i<ongoingTouches.length; i++) {
var id = ongoingTouches[i].identifier;
if (id == idToFind) {
return i;
}
}
return -1; // not found
}
As you can see, this function either returns the index of a given id (if a particular Touch hasn't ended yet, since the handler was last called), or returns -1.
The code page you linked to is pretty straight forward, IMO: Each time a new touchstart event is registered, the id's of on-going (or past) touch events are checked and the corresponding ids are removed from a global (evil) array. There's a ton of reasons why this is done, but I'll leave those for you to find (or ask a follow-up question about, once you've tried to familiarize yourself with the code).
The identifier is unique for each distinct changedtouch. Now, you didn't provide the code you're using, but my guess would be that you're not getting the unique identifier from the right place:
var ids = [];
for (var i=0;i<e.changedTouches.length;i++)
{
ids.push(e.changedTouches[0].identifier);
}
console.log(ids);
If you're touching with more than 1 digit, ids should contain a distinct id for each touch.
I might be barking up the wrong tree altogether here, but again: add your code to the question so we can be sure. But you could be reassigning the id over and over, only leaving you with the value for the last Touch in the changedTouches array.
You could also be trying to get the identifier directly from the event object. Or you could be doing something else, all the same.
Check your console, log the event object and check the changedTouches property of that event.

Javascript caching tools and techniques

I have a requirement where in I want to loop through all the DropDown (select) controls on a page. I am picking all the DropDown controls using:
var DropDowns = document.getElementByTagName('select');
I loop through these DropDowns to set an option programatically of few controls:
for (i=0; i<= DropDowns.Length; i++)
{
var CurrentControl = DropDowns[i];
CurrentControl.options[CurrentControl.selectedIndex].value = 1;
}
Is there any Javascript framework which supports caching? I want to cache all the DropDown control names and loop through the cached names instead of directly looping through Document object.
Are there any tricks to increase the performance of the loop?
I don't think that you're going to see huge improvements in performance for a loop that is only iterating through 30-40 elements, but some browsers will get a huge speed boost from looping through an array instead of a NodeList or HTMLCollection as it is called in some browsers that implement DOM level 1.
So, to answer your question, you can "cache" the objects etc. in an array and it should speed up that loop for future iterations.
Be aware that you need to keep this array up-to-date because it is not "live" like the DOM is.
In jquery, it would look something like this:
var $menus = $('select');
$.each($menus, function(index, menu) {
// Do whatever you want to the menu here
};
I am suggesting a different approach -
Save the options in a hidden field with a comma separated values and on page load set the values of control by parsing the , separated values.
I think you're already doing this much more efficiently than some of the answers describe. Bear in mind you're not continuously looping through the document, you're looping through your cached NodeList of DropDowns. If you want to change each one of these elements, then you will inevitably need to loop through them to change them individually.

jQuery "Autocomplete" plugin is messing up the order of my data

I'm using Jorn Zaefferer's Autocomplete plugin on a couple of different pages. In both instances, the order of displayed strings is a little bit messed up.
Example 1: array of strings: basically they are in alphabetical order except for General Knowledge which has been pushed to the top:
General Knowledge,Art and Design,Business Studies,Citizenship,Design and Technology,English,Geography,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Displayed strings:
General Knowledge,Geography,Art and Design,Business Studies,Citizenship,Design and Technology,English,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Note that Geography has been pushed to be the second item, after General Knowledge. The rest are all fine.
Example 2: array of strings: as above but with Cross-curricular instead of General Knowledge.
Cross-curricular,Art and Design,Business Studies,Citizenship,Design and Technology,English,Geography,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Displayed strings:
Cross-curricular,Citizenship,Art and Design,Business Studies,Design and Technology,English,Geography,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Here, Citizenship has been pushed to the number 2 position.
I've experimented a little, and it seems like there's a bug saying "put things that start with the same letter as the first item after the first item and leave the rest alone". Kind of mystifying. I've tried a bit of debugging by triggering alerts inside the autocomplete plugin code but everywhere i can see, it's using the correct order. it seems to be just when its rendered out that it goes wrong.
Any ideas anyone?
max
EDIT - reply to Clint
Thanks for pointing me at the relevant bit of code btw. To make diagnosis simpler i changed the array of values to ["carrot", "apple", "cherry"], which autocomplete re-orders to ["carrot", "cherry", "apple"].
Here's the array that it generates for stMatchSets:
stMatchSets = ({'':[#1={value:"carrot", data:["carrot"], result:"carrot"}, #3={value:"apple", data:["apple"], result:"apple"}, #2={value:"cherry", data:["cherry"], result:"cherry"}], c:[#1#, #2#], a:[#3#]})
So, it's collecting the first letters together into a map, which makes sense as a first-pass matching strategy. What i'd like it to do though, is to use the given array of values, rather than the map, when it comes to populating the displayed list. I can't quite get my head around what's going on with the cache inside the guts of the code (i'm not very experienced with javascript).
SOLVED - i fixed this by hacking the javascript in the plugin.
On line 549 (or 565) we return a variable csub which is an object holding the matching data. Before it's returned, I reorder this so that the order matches the original array of value we were given, ie that we used to build the index in the first place, which i had put into another variable:
csub = csub.sort(function(a,b){ return originalData.indexOf(a.value) > originalData.indexOf(b.value); })
hacky but it works. Personally i think that this behaviour (possibly coded more cleanly) should be the default behaviour of the plugin: ie, the order of results should match the original passed array of possible values. That way the user can sort their array alphabetically if they want (which is trivial) to get the results in alphabetical order, or they can preserve their own 'custom' order.
What I did instead of your solution was to add
if (!q && data[q]){return data[q];}
just above
var csub = [];
found in line ~535.
What this does, if I understood correctly, is to fetch the cached data for when the input is empty, specified in line ~472: stMatchSets[""] = []. Assuming that the cached data for when the input is empty are the first data you provided to begin with, then its all good.
I'm not sure about this autocomplete plugin in particular, but are you sure it's not just trying to give you the best match possible? My autocomplete plugin does some heuristics and does reordering of that nature.
Which brings me to my other answer: there are a million jQuery autocomplete plugins out there. If this one doesn't satisfy you, I'm sure there is another that will.
edit:
In fact, I'm completely certain that's what it's doing. Take a look around line 474:
// loop through the array and create a lookup structure
for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
/* some code */
var firstChar = value.charAt(0).toLowerCase();
// if no lookup array for this character exists, look it up now
if( !stMatchSets[firstChar] )
and so on. So, it's a feature.

Categories