Is there a way to access enter and update selections together? - javascript

This seems to be a regular pattern for me when using d3.js:
function getAttr(d,i) { ... }
things = container.selectAll("selector").data(data);
things.attr("attr", getAttr);
things.enter().append("selector").attr("attr", getAttr);
Is there a better way to do this?

Yes, in recent versions of D3, the .enter() selection merges into the update selection after you've processed it. That is, instead of
things = container.selectAll("selector").data(data);
things.attr("attr", getAttr);
things.enter().append("selector").attr("attr", getAttr);
you can write
things = container.selectAll("selector").data(data);
things.enter().append("selector");
things.attr("attr", getAttr);
Not that much shorter, but it saves you quite a bit of code if you're setting a large number of different attributes, handlers, etc.

Related

How to cut off function from global scope

I have an idea for a game where people can type in some simple instructions for their character like player.goLeft() or player.attackInFront() and for that I have people type their code into a text box and then I parse it into eval(). This works well but it also allows people to change their own character object by typing things like player.health = Infinity; or something similar. I have a list of functions I want to allow people to use, but I am unsure how to restrict it to only use them.
I understand that the whole point of not letting people use eval is to avoid accidental cross-site scripting but I am unsure on how else to do this. If you have a suggestion please leave a comment about that.
I asked some people around on what to do and most suggested somehow changing scope(which is something I was not able to figure out) or to add some odd parameter to each function in my code that would be required to be a specific string to execute any function, but that seems hacky and since I am making the game in browser with p5js it would be easy to just inspect element and see what the password is.
basically every character has variable called "instruction" which is just a string of javascript. Then every frame of the game I execute it by doing eval(playerList[i].instruction);
tl;dr, how can I only allow specific function to be executed and not allow any others?
EDIT: I forgot to mention that I also am planning to provide player with information so that people can made code that would adapt to the situation. For example there will be parameter called vision that has vision.front and vision.left etc. These variables would just say if there is an enemy, wall, flower, etc around them in a grid. Some people suggested that I just replace some functions with key words but then it compromises the idea of using if statements and making it act differently.
EDIT 2: Sorry for lack of code in this post, but because of the way I am making it, half of the logic is written on server side and half of it works on client side. It will be a little large and to be completely honest I am not sure how readable my code is, still so far I am getting great help and I am very thankful for it. Thank you to everybody who is answering
Do NOT use eval() to execute arbitrary user input as code! There's no way to allow your code to run a function but prevent eval() from doing the same.
Instead, what you should do is make a map of commands the player can use, mapping them to functions. That way, you run the function based on the map lookup, but if it's not in the map, it can't be run. You can even allow arguments by splitting the string at spaces and spreading the array over the function parameters. Something like this:
const instructions = {
goLeft: player.goLeft.bind(player),
goRight: player.goRight.bind(player),
attackInFront: player.attackInFront.bind(player)
};
function processInstruction(instruction_string) {
const pieces = instruction_string.split(' ');
const command = pieces[0];
const args = pieces.slice(1);
if (instructions[command]) {
instructions[command](...args);
} else {
// Notify the user their command is not recognized.
}
};
With that, the player can enter things like goLeft 5 6 and it will call player.goLeft(5,6), but if they try to enter otherFunction 20 40 it will just say it's unrecognized, since otherFunction isn't in the map.
This issue sounds similar to the SQL Injection problem. I suggest you use a similar solution. Create an abstraction layer between the users input and your execution, similar to using parameters with stored procedures.
Let the users type keywords such as 'ATTACK FRONT', then pass that input to a function which parses the string, looks for keywords, then passes back 'player.attackInFront()' to be evaluated.
With this approach you simplify the syntax for the users, and limit the possible actions to those you allow.
I hope this isn't too vague. Good luck!
From your edit, it sounds like you're looking for an object-oriented approach to players. I'm not sure of your existing implementation needs, but it would look like this.
function Player() {
this.vision = {
left: '',
// and so on
}
}
Player.prototype.updateVisibilities = function() {
// to modify the values of this.visibility for each player
}
Player.prototype.moveLeft = function() {
}
Don't give the user an arbitrary interface (such as an input textfield that uses eval) to modify their attributes. Make a UI layer to control this logic. Things like buttons, inputs which explicitly run functions/methods that operate on the player. It shouldn't be up to the player as to what attributes they should have.

First .slice() and then do things with all other elements

I was quite sure that I already did this in some earlier version of jQuery, but http://api.jquery.com/category/traversing/ seems to suggest otherwise.
What I'm looking for is similar kind of the opposite of .addBack() - a traversing function that uses "all other" elements (not .not()!)
Preusdo Example:
$('.some-class li').slice(33,55).hide().allOthers().show()
Edit: This is not actually a hide() / show() based problem, this is just a simple example to clarify what I meant.
First, I'ld like to manipulate a set of elements selected with .slice(), and then manipulate all elements that were not selected by .slice().
Is there a handy traversing function I've missed that does just that? I know how to solve it in general, but a ".allOthers()" method that I might have missed would certainly be more handy and clearer.
In your case you can just call show before calling slice
$('.some-class li').show().slice(33,55).hide();
It's true that there is no method to get all others, the closest is to get back they previous collection as you mentioned, http://api.jquery.com/addback/
You could implement a plugin, since I'm on my mobile, I'll just write some straight code
// o(n*m), could be improved
function allOthers(jqObj) {
var current = [].concat(jqObj);
var prev = jqObj.addBack();
return prev.filter(function(obj){
return !current.includes(obj);
});
}
First show all of them and then hide from 33 to 55, here is the demo
$('.some-class li').show().slice(33,55).hide();
After testing #JuanMendes suggestion, I played around with it a bit and found quite a compact way to implement this kind of functionality, due to jQuery's prevObject:
$.fn.others = function() {
return this.prevObject.not( this );
}
I didn't test it too much with other methods, so it might needs some further changes - but it seems to work fine with .slice() at least.
https://jsfiddle.net/1L3db7k4/

jQuery Nested Methods

Excuses if this has been posted, not sure how to search for such a thing.
I'm working on a website and I'd like to know what current industry conventions are. I am more concerned if there is a reason to avoid calling a method inside of another method.
Below I've placed a few examples, please let me know which one is best.
First, easy example on two lines:
var formattedRole = HTMLheaderRole.replace("%data%", bio.role);
$("#header").prepend(formattedRole);
What about all on one line?
$("#header").prepend(HTMLheaderRole.replace("%data%", bio.role));
Let's get a little more complicated:
var formattedEmployer = HTMLworkEmployer.replace("%data%", work.jobs[job].employer);
var formattedTitle = HTMLworkTitle.replace("%data%", work.jobs[job].title);
var formattedConcat = formattedEmployer.concat(formattedTitle);
$(".work-entry:last").append(formattedConcat);
How about just condensing one of those lines?
var formattedEmployer = HTMLworkEmployer.replace("%data%", work.jobs[job].employer);
var formattedTitle = HTMLworkTitle.replace("%data%", work.jobs[job].title);
$(".work-entry:last").append(formattedEmployer.concat(formattedTitle));
Thanks tons for the input!
Technically, avoiding the extra variable assignments in both of the first examples is slightly more efficient. However, the amount of resources saved is so trivial that you would have to be running the function thousands of times on a particularly old mobile device to see and difference. That said, readability is king when comparing two segments of code that have nearly identical performance. Choose whichever option is easier for you or some other maintainer to read.

how to search and replace through an entire block of text?

I'm trying to replace every image tag in a block of text with a unique string. So far I've tried to get the index of the beginning and end of a tag, create a substring, and then replace the substring. The problem is that I cannot do this an infinite number of times (the text block itself can be long with an n number of image tags).
Here is my code so far:
var txtBlock = currBlock.getElementsByClassName("txtContent")[0];
var imgStartPoint = txtBlock.indexOf("<img ");
var imgEndPoint = txtBlock.indexOf(" />");
var imgstring = txtBlock.substring(imgStartPoint, imgEndPoint);
How can I repeat this process n number of times?
The best way to approach this problem, and most programming problems in general, is to think about what you need to do and write out the steps that you need to perform in order to solve your problem in plain English.
To get you started, you should probably think about the following:
How many times does the code need to execute? How do you determine this?
How does the algorithm know that it is done? Can you think of a couple ways to achieve this?
Once you have a decent logical plan, the code will be much easier to write.
In general, break the problem down to smaller tasks and you should be able to tackle almost any programming problem, regardless of language, etc.
Let me know if you need further help.
It seems that you get your data from a DOM. So you can make yourself familiarly with the DOM operations and replace all image nodes with text nodes.
Helpful methodes:
DOM Document getElementsByTagName Method -
http://w3schools.com/jsref/met_document_getelementsbytagname.asp
DOM Node replaceChild Method -
http://w3schools.com/jsref/met_node_replacechild.asp
DOM Document createTextNode Method -
http://w3schools.com/jsref/met_document_createtextnode.asp

conditionals in javascript based on page currently showing

because of some problems with joomla "in-content javascript" I have to give all my js logic to one file, but there are problems with inconsistence of dom elements across my site (it is ajax driven, so there is only one script and various DOMs).
What is the best solution to make some conditionals solving this problem..
Is it checking $(selector).length, or is there any better solution..
And in case of the $(selector).length , is there a way to save this selector to variable (performance issues)
for example some kind of
var selector = ($(selector).length !== 0) ? this : false ;
if(selector) { makeSomething; }
The this is actually pointing to Window object..So is there any way to make it like this without need of reselection?
Thanks
var $obj = $('selector');
if ($obj.length) { makeSomething(); }
Actually, this is only meaningful if you are searching for the existence of a certain element (that might identify a whole page) and running several operations based on that.
If you just want to do something on the elements like
$('selector').append('x');
the condition might be useless, because if the jQuery collection is empty, the methods won't run anyways (as pointed out by #Gary Green).

Categories