I'm trying to parse HTML code in jquery, get an attribute of it and extract a number from the id, and then append it to a string like that:
function sortHTMLContentByStrength(x, y) {
console.log($($.parseHTML(x)));
let strength1 =
parseFloat(($("#team-modal-strength-" + $($.parseHTML(x)[1]).attr("id").match(/\d+/)[0]).html()));
let strength2 =
parseFloat(($("#team-modal-strength-" + $($.parseHTML(y)[1]).attr("id").match(/\d+/)[0]).html()));
return strength2 - strength1;
}
The real problem is:
As you can see, i get this S.fn.init(2). Every single console log looks like this. I roughly know what it means and also it wouldn't be a big deal because I used an index to retrieve only the second element (tr tag) which I'm interested in like that:
$($.parseHTML(x)[1])
I'm just getting the second element of the returned array which is tr tag. This code worked and there was nothing wrong with up until now.
So, now the biggest problem. When I run the same code once more later, I don't receive an array of two objects anymore. I get this:
It's not an array anymore so the indexing trick $($.parseHTML(x)[1]) doesn't work anymore.
I really need to call this sorting function multiple times in my program but due to the issue the app breaks. I would be very thankful for any hints on that.
Related
I'm using charts.js, where I want to add one datarow per minute. Hence to avoid a complete redraw, Im using some ajax and just pushing a new element to the chart.
Pushing VALUES works fine, however, pushing to the label-array shows very strange behaviour. I'v now tried everything from a simple push upto iteratively cloning the whole array, copy all values, replacing the whole array... The result is still the same.
The added element seems to be always end up with index 0, therefore ending up left in the chart, rather than on the right side.
Upon initial pageload, the data that is existing is loaded as a json-array, which works as expected, for example:
var labels = ["16:00", "16:01", "16:02"]
Now, using some ajax, I retrieve a new Dataset for 16:03. Pushing that label to the array like this:
...
labels.push("16:03");
console.log(labels);
...
and inspecting it in the browser afterwards leads to the following strange view:
The stringified representation looks as expected:
(4) ["16:00", "16:01", "16:02", "16:03"]
But when expanding the view in chrome, the result is:
0: "16:03"
1: "16:00"
2: "16:01"
3: "16:02"
So, iterating the array by using index-values obviously leads to a different result than using .toString(). I have no idea what is happening here. I'm mainly confused, why the stringified version looks different than the actual drill down on indexes?
Running a vanilla-example of these steps leads to the desired result. So it has to do something with the "context" of that array. But I have no idea where to start digging ;)
Here's a screenshot
edit:
Following the example over here, it should work like that...
https://www.chartjs.org/docs/latest/developers/updates.html
Comment of #CBroe made me figure it out:
Uppon initial loading, I did not outline the labels explicit, because there i'm having whole objects like {x:"16:00", temp="20", rain="0"} i'm feeding the datarows of chart.js with.
Now, when altering the label-array, chart-js seems to apply the following logic:
Labels-Array is a dedicated Array that EXISTS but is empty if not explicit defined.
In the Getter of that array, Any Labels derived from data-objects are appended to the array.
hence, pushing to the actual array starts with index "0" if it's inititially empty. But looking at the result of the getter then delivers the static array + any required label derived from the data objects given.
Now outlining the labels-array explicit as well, this resolves the issue.
(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.
I have a test I am trying to create where I change the name of a card. The problem is there is a hidden error card which has the exact same identifiers as what I am trying to edit. The test returns that the object I am trying to work with is unreachable, which makes sense, it is under the one I am working with.
The locator which I have is: textarea[ng-model="ctrl.currentChartTitle"].
Yes, I know that I can do this by model and tried that, but it yields the same results.
Here is a screenshot of the html the yellow is the on top object I am trying to reach:
Not really sure how do to do the selector for this, so it always works.
So I did a bit more searching and found a solution. It looks a like this:
chartTitleTextArea = this.visualizer.all(by.css('textarea[ng-model="ctrl.currentChartTitle"]')).filter((element) => {
return element.isDisplayed().then((isDisplayed) => {
return isDisplayed
})
}).first()
Basically, what this is doing is getting all of the elements which match that locator. Then filtering them on if isDisplayed() is true. Then grabbing the first one.
I am trying to make a page work for my website using the mootools framework. I have looked everywhere I can think of for answers as to why this isn't working, but have come up empty.
I want to populate several arrays with different data types from the html, and then, by calling elements from each array by index number, dynamically link and control those elements within functions. I was testing the simple snippet of code below in mootools jsfiddle utility. Trying to call an element from array "region" directly returns "undefined" and trying to return the index number of an element returns the null value of "-1".
I cannot get useful data out of this array. I can think of three possible reasons why, but cannot figure out how to identify what is really happening here:
1. Perhaps this array is not being populated with any data at all.
2. Perhaps it is being populated, but I am misunderstanding what sort of data is gotten by "document.getElementBytag()" and therefore, the data cannot be displayed with the "document.writeln()" statement. (Or am I forced to slavishly create all my arrays?)
3. Perhaps the problem is that an array created in this way is not indexed. (Or is there something I could do to index this array?)
html:
<div>Florida Virginia</div>
<div>California Nevada</div>
<div>Ohio Indiana</div>
<div>New York Massachussetts</div>
<div>Oregon Washington</div>
js:
var region = $$('div');
document.writeln(region[2]);
document.writeln(region.indexOf('Ohio Indiana'));
Thanks for helping a js newbie figure out what is going on in the guts of this array.
$$ will return a list of DOM elements. If you are only interested in the text of those DOM nodes, then extract that bit out first. As #Dimitar pointed out in the comments, calling get on an object of Elements will return an array possibly by iterating over each element in the collection and getting the property in question.
var region = $$('div').get('text');
console.log(region[2]); // Ohio Indiana
console.log(region.indexOf('Ohio Indiana')); // 2
Also use, console.log instead of document.writeln or document.write, reason being that calling this function will clear the entire document and replace it with whatever string was passed in.
See an example.
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.