How do you program game states in javascript? - javascript

You can use frames in Flash ActionScript to hold a welcome screen, game content, and a game over screen; you can create an enum of game states in Microsoft XNA and draw all graphics and text when you call that state in the Draw method. How would you do this in HTML5 and JavaScript? I have a feeling that this can be done by showing and hiding div's, but I'm not entirely sure how to do it.
<asp:DropDownList ID="DDL_SelectGame" runat="server" CssClass="BigText">
<asp:ListItem>MatchMe (Memory)</asp:ListItem>
<asp:ListItem>Royal Jewels (Bejeweled)</asp:ListItem>
<asp:ListItem>Tetris</asp:ListItem>
</asp:DropDownList>
<div id="Welcome" class="Welcome">
<asp:Button ID="BTN_StartGame" runat="server" CssClass="Button" />
</div>
<div id="Game" class="Game">
</div>
<div id="GameOver" class="GameOver">
<asp:Button ID="BTN-Replay" CssClass="Button" runat="server" />
</div>
The code above is an example of how I would set up a game page on my site (http://www.graphics-geek.com).

Here's a state machine I'm planning to use for future game development.
With something like this you should be able to implement the drawing of different states, and state changes in the callbacks. Either by show/hide divs, or drawing different buffers/graphics to a canvas.
To create states:
// initially hide the divs using css or js
state.add("welcome",
function () {
document.getElementById("Welcome").style.display = "block";
},
function () {
document.getElementById("Welcome").style.display = "none";
}
);
state.add("level01",
function () {
document.getElementById("Game").style.display = "block";
},
function () {
document.getElementById("Game").style.display = "none";
}
);
state.add("end",
function () {
document.getElementById("GameOver").style.display = "block";
}
);
The second function is optional (actually both are, but such a state would do nothing).
To switch states (assuming we have added 3 states; "welcome", "level01" and "end"):
state.enter("welcome");
//... at some point welcome changes state using following:
state.enter("level01");
//... at the end of level1:
state.enter("end");
Code:
var state = (function () {
"use strict";
var currentState = -1,
stateNames = [],
stateCallbacks = [];
return {
current: function () {
if (currentState >= 0) {
return stateNames[currentState];
}
},
add: function (name, onEnter, onExit) {
var index = stateNames.indexOf(name);
if (index !== -1) {
throw "State " + name + " already exist!";
}
stateCallbacks.push({
enterState: onEnter || false,
exitState: onExit || false
});
stateNames.push(name);
},
remove: function (name) {
var index = stateNames.indexOf(name);
if (index === -1) {
throw "State " + name + " not found!";
}
stateNames.splice(index, 1);
stateCallbacks.splice(index, 1);
},
enter: function (name) {
var index = stateNames.indexOf(name);
if (index === -1) {
throw "State " + name + " not found!";
}
if (stateCallbacks[currentState].exitState) {
stateCallbacks[currentState].exitState();
}
currentState = index;
if (stateCallbacks[index].enterState) {
stateCallbacks[index].enterState();
}
},
exit: function () {
if (currentState === -1) {
throw "Not currently in any state";
}
if (stateCallbacks[currentState].exitState) {
stateCallbacks[currentState].exitState();
}
currentState = -1;
}
};
}());
Additional comments:
The structure with the anonymous function wrapping the code is a module pattern for javascript from http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html. This is similar to a namespace in other languages.
For possibly better performance of lookups (implemented here as Array.indexOf(name)), you might want to add an object with string keys associated with index values, especially for large number of states (ie. a game with 100+ levels, or a different use case where states are switched more intensively). I've not benchmarked this though, so it is highly speculative. In theory an array search is n - 1 times slower than a hashtable lookup (which objects are likely implemented as).
Edit:
Fixed bug in code. state.add now checks for existing names.
Added disclaimer.
Added example of how to show/hide divs from callbacks.
Removed disclaimer, replaced code with improved version.

Related

What is the best way to implement undo state change (undo store/history implementation) in React Redux

I'm looking out for a history redo/ store reset to the previous state in a redux react application.
I've found a blog telling it can be done by storing present, future and past states in a stack and resetting accordingly.
I've also found a similar question in StackOverflow, but it doesn't give me a proper answer or maybe it's difficult for me to understand.
I've built a demo ToDo app and have used redux-logger to log store details with previous state and updated state. You can find the code here.
Do we have a store reset method in redux, so that we can get the previous state and update the store other that having a store with the present, the past and future states?
For anyone looking for a solution to this in 2020. You don't have to store the entire state object as the present, past, and future.
Instead, you can just store details about what has changed. This can be implemented using ImmerJS. It records all changes done on the state object and generates something called a patch.
Example: If age is updated from 32 to 40, then the generated patch will be:
Patch:
[ { op: 'replace', path: [ 'age' ], value: 40 } ]
Inverse Patch: [ { op: 'replace', path: [ 'age' ], value: 32 } ]
It also exposes a method to apply these patches/inverse patches to the state - applyPatch. So, to undo we can apply an inverse patch and to redo we can apply a patch.
You can find details of full implementation here: Implementing Undo-Redo Functionality in Redux using Immer
What is the best way ...
Best way is always difficult to define, it really depends of your use-case and requirements included client and server.
But to get start you you could consider using a library or looking how they approach this problem, some example:
https://github.com/omniscientjs/immstruct
https://www.npmjs.com/package/redux-undo
https://github.com/PowToon/redux-undo-redo
Tutorial with example of todo undo/redo in redux:
https://github.com/reactjs/redux/tree/master/examples/todos-with-undo
Or you could implement your own, as redux you could store all your application state. A simple stack could be a simple and efficient way to store your app state at any given time.
let yourHistory = [state1, state2, state3];
I created a state undo/redo snapshot manager class, which would be great for tracking the change history on an HTML element.
<div id="buttons">
<button type="button" id="undo_btn">Undo</button>
<button type="button" id="redo_btn">Redo</button>
</div>
<br/><br/>
<div id="content">
<label>
Input1:
<input type="text" value="" />
</label>
<br/><br/>
<label>
Input2:
<input type="text" value="" />
</label>
<br/><br/>
<label>
Input3:
<input type="text" value="" />
</label>
<br/><br/>
<label>
Input4:
<input type="text" value="" />
</label>
<br/><br/>
</div>
<script type="text/javascript">
var StateUndoRedo = function() {
var init = function(opts) {
var self = this;
self.opts = opts;
if(typeof(self.opts['undo_disabled']) == 'undefined') {
self.opts['undo_disabled'] = function() {};
}
if(typeof(self.opts['undo_enabled']) == 'undefined') {
self.opts['undo_enabled'] = function() {};
}
if(typeof(self.opts['redo_disabled']) == 'undefined') {
self.opts['redo_disabled'] = function() {};
}
if(typeof(self.opts['redo_enabled']) == 'undefined') {
self.opts['redo_enabled'] = function() {};
}
if(typeof(self.opts['restore']) == 'undefined') {
self.opts['restore'] = function() {};
}
self.opts['undo_disabled']();
self.opts['redo_disabled']();
}
var add = function(state) {
var self = this;
if(typeof(self.states) == 'undefined') {
self.states = [];
}
if(typeof(self.state_index) == 'undefined') {
self.state_index = -1;
}
self.state_index++;
self.states[self.state_index] = state;
self.states.length = self.state_index + 1;
if(self.state_index > 0) {
self.opts['undo_enabled']();
}
self.opts['redo_disabled']();
}
var undo = function() {
var self = this;
if(self.state_index > 0) {
self.state_index--;
if(self.state_index == 0) {
self.opts['undo_disabled']();
} else {
self.opts['undo_enabled']();
}
self.opts['redo_enabled']();
self.opts['restore'](self.states[self.state_index]);
}
}
var redo = function() {
var self = this;
if(self.state_index < self.states.length) {
self.state_index++;
if(self.state_index == self.states.length - 1) {
self.opts['redo_disabled']();
} else {
self.opts['redo_enabled']();
}
self.opts['undo_enabled']();
self.opts['restore'](self.states[self.state_index]);
}
}
var restore = function() {
var self = this;
self.opts['restore'](self.states[self.state_index]);
}
var clear = function() {
var self = this;
self.state_index = 0;
//self.states = [];
}
return {
init: init,
add: add,
undo: undo,
redo: redo,
restore: restore,
clear: clear
};
};
//initialize object
var o = new StateUndoRedo();
o.init({
'undo_disabled': function() {
//make the undo button hidden
document.getElementById("undo_btn").disabled = true;
},
'undo_enabled': function() {
//make the undo button visible
document.getElementById("undo_btn").disabled = false;
},
'redo_disabled': function() {
//make the redo button hidden
document.getElementById("redo_btn").disabled = true;
},
'redo_enabled': function() {
//make the redo button visible
document.getElementById("redo_btn").disabled = false;
},
'restore': function(state) {
//replace the current content with the restored state content
document.getElementById("content").innerHTML = state;
}
});
//initialize first state
o.add(document.getElementById("content").innerHTML);
o.restore();
o.clear();
//bind click events for undo/redo buttons
document.getElementById("undo_btn").addEventListener("click", function() {
o.undo();
});
document.getElementById("redo_btn").addEventListener("click", function() {
o.redo();
});
//bind change events for content element
document.getElementById('content').addEventListener("change", function(event) {
// the following is required since vanilla JS innerHTML
// does not capture user-changed values of inputs
// so we set the attributes explicitly (use jQuery to avoid this)
var elems = document.querySelectorAll("#content input");
for(var i = 0; i < elems.length; i++) {
elems[i].setAttribute("value", elems[i].value);
}
//take a snapshot of the current state of the content element
o.add(document.getElementById("content").innerHTML);
});
</script>
See this JSFiddle: https://jsfiddle.net/up73q4t0/56/

$BROADCASTing from service to controller

I have a little concern here
This comes from a service named BetSlipFactory
removeSlip: function(slip) {
return betSlipSelectionRequest('/betSlip/removeSelection', {
game: slip.game,
pair: slip.pair,
line: slip.line
});
}
Then I have this function in the controller for that service
$scope.removeSlip = function(slip) {
$rootScope.$broadcast('betSlip:removeLines', slip);
BetSlipFactory.removeSlip(slip)
}
Next I have a controller in a different scope named LinesCtrl and I have this function here which calls a couple functions from the service BetSlipFactory which is like a kind of toggle function
$rootScope.$on('betSlip:removeLines', function(event, slip) {
if (slip) {
BetSlipFactory.remove(line, row, type);
};
});
$scope.addLineToBetSlip = function(line, row, type) {
var spreadSelected = (row.spreadSelected && type === 'spread'),
totalSelected = (row.totalSelected && type === 'total'),
moneyLineSelected = (row.moneyLineSelected && type === 'moneyline');
if (spreadSelected || totalSelected || moneyLineSelected) {
BetSlipFactory.remove(line, row, type);
}else {
BetSlipFactory.add(line, row, type);
}
};
And then the HTML:
<button ng-click="removeSlip(slip)"></button>
And:
<td ng-class="!row.moneyLineSelected ? 'lines-hover' : 'line-selected'">
<a ng-click="addLineToBetSlip(line, row, 'moneyline')">
<span ng-hide="row.noMoneyLine">{{:: row.moneyLine}}</span>
</a>
</td>
What I need: combine the scopes, when the function $scope.removeSlip(slip) is call, also I need to call $scope.addLineToBetSlip(line, row, type) and then that function should call BetSlipFactory.remove(line, row, type); as it is within that if statement.
When I call $scope.removeSlip(slip) I need to kill slip parameter, within the scope of BetSlipFactory everything works great.
I recorded a video for you to see what I am talking about, let me explain the video a little bit.
In the first 2 tries you might see that I am able to select and deselect and everything works great, but in the 3rd and 4th try, you see that I select a line, and then I go a call and removeSlip(slip) when I play the X on the right, and in order to deselect the line on the left I have to do it manually.
So I started a fiddle showing this process dumbed way down compared to the plnkr you started after. Here I am using two separate controllers and a service (factory) to manage the data. This can be done without using $rootScope or $broadcast. Hopefully you can take what I have done here and integrate it into all that code you posted on plnkr. Below you can see it is quite a simple process
the jsfiddle
HTML:
<div ng-app="TestApp">
<div id="colLeft" ng-controller="LeftController">
<div ng-repeat="bet in possibleBets">
<button ng-class="!bet.moneyLineSelected ? 'lines-hover' : 'line-selected'" ng-click="addLineToBetSlip(bet)">{{bet.name}}</button>
</div>
</div>
<div id="colRight" ng-controller="RightController">
Your Bets:<br>
<div ng-repeat="bet in bets">
Active bet: {{bet.name}} - <button ng-click="removeLineFromBetSlip(bet)">×</button>
</div>
</div>
</div>
CSS:
.lines-hover {
}
.line-selected {
background:yellow;
}
#colLeft {
width:65%;
background:#f00;
float:left;
}
#colRight {
width:35%;
background:gray;
float:left;
}
and finally the JS
var app = angular.module('TestApp',[]);
app.controller('LeftController', function($scope, BetSlipFactory)
{
// this data is the data from your DB
$scope.possibleBets = [
{name:'Bet 1',moneyLineSelected:false},
{name:'Bet 2',moneyLineSelected:false},
{name:'Bet 3',moneyLineSelected:false}
];
// now that I think about it, addLineToBetSlip is not a good name
// since it actually toggles the bet
$scope.addLineToBetSlip = function(bet)
{
bet.moneyLineSelected = !bet.moneyLineSelected; // toggle the moneyLineSelected boolean
(bet.moneyLineSelected) ? BetSlipFactory.add(bet) : BetSlipFactory.remove(bet); // add or remove the bet
};
});
app.controller('RightController', function($scope, BetSlipFactory)
{
$scope.bets = BetSlipFactory.getAllBets(); // link to all the active bets
// remove the bet from the factory
$scope.removeLineFromBetSlip = function(bet)
{
bet.moneyLineSelected = false;
BetSlipFactory.remove(bet);
};
});
app.service('BetSlipFactory', function()
{
//a place to keep active bets
var theBets = [];
return {
add: function(bet)
{
// actually add the bet to this local array
theBets.push(bet);
},
remove: function(bet)
{
// you should do error checking of the index before removing it
var index = theBets.indexOf(bet);
theBets.splice(index,1);
},
getAllBets: function()
{
//simply return all active bets
return theBets;
}
}
});
function log(msg)
{
console.log(msg);
}

Get this._id of collection instance in template.foo.rendered

I try to include the rating module from Semantic UI (http://semantic-ui.com/modules/rating.html) under articles for users to be able to rate them. If user rates the article, the article id is stored into Meteor.user().profile.ratedItems.
If user goes away from the article to another one and then comes back to the first one the rating module should be rendered as read-only(so the user is not able to rate the same article again).
The problem is that I do not know how to check if article _id is stored in Meteor.user().profile.ratedItems in template.foo.rendered because this._id gives not article id but id of template.
In template.foo.events and template.foo.helpers I can check for that by the sentence _.contains(Meteor.user().profile.ratedItems,this._id) and it works OK everywhere but of cause not in template.foo.rendered. Now even if user rates an article more then one time the rating in db does not change. But I need to solve the "visual" issue.
So here is the code:
JS:
Template.foo.helpers({
rate: function () {
return Math.floor(this.rating);
},
state : function () {
if (Meteor.userId()) {
if (_.contains(Meteor.user().profile.ratedItems,this._id)) {
return "rated"
} else {return "unrated"}
} else {
return ""
}
},
statetext: function () {
if (Meteor.userId()) {
if (_.contains(Meteor.user().profile.ratedItems,this._id)) {
return "Overall rating:" }
else { return "Rate the article:"}
} else {
return "Overall rating:"
}
}
});
Template.foo.rendered = function() {
if (Meteor.userId()) {
if (_.contains(Meteor.user().profile.ratedItems,this._id)) {
$('.ui.rating').rating('disable');
} else {
$('.ui.rating').rating();
}
} else {
$('.ui.rating').rating('disable');
}
};
Template.foo.events({
'click .unrated': function () {
var addedRating = $('.unrated').rating('get rating');
var currentArticleId = this._id;
var newsum = this.rating_sum+addedRating;
var newcount = this.rating_count+1;
var newrating = newsum/newcount;
Schools.update(currentSchoolId,{$inc: {rating_count:1}});
Schools.update(currentSchoolId,{$inc: {rating_sum:addedRating}});
Schools.update(currentSchoolId,{$set: {rating:newrating}});
Meteor.users.update({_id:Meteor.userId()},{$push: {'profile.ratedItems':currentArticleId}});
$('.ui.rating').rating({"set rating":Math.floor(newrating)});
$('.ui.rating').rating('disable');
}
});
HTML:
<template name="schoolPage">
<div class="ui center aligned blue segment">
{{#if currentUser}}{{statetext}}{{else}}Overall rating:{{/if}}
<div class="ui tiny heart rating {{state}}" data-rating="{{rate}}" data-max-rating="5"></div>
</div>
</template>
I thought about using Session.set and Session.get but did not come to any solution yet.
Thank you for help in advance.
Instead of this, you can use Template.currentData inside the rendered callback.
See the docs at http://docs.meteor.com/#/full/template_currentdata.

Using jQueryUI to get boolean from user

I'm writing some form validation functions, and I've decided to go with jQueryUI for prompting the user because of flexibility.
There is a slight problem tho. I want my functions to return an array which consists of a boolean and a string for my error reporting system. JQueryUI dialogs are asynchronous which means the browser won't hang and wait for a return value as the native prompt() would.
Here is some sample code:
Validator function:
function verifyTOS_PVM_v2()
{
verifyTOS_PVM_v2_callback = '';
if(!empty($('#inputPVM').val())) {
$('#inputPVM').val(date('d.m.Y', parseFinnishDate($('#inputPVM').val())));
val = $('#inputPVM').val()
date = parseFinnishDate($('#inputPVM').val());
today = today();
diff = Math.floor((date - today)/60/60/24);
if(diff <= -14)
{
buttons =
[
{
text:"Kyllä",
click:function()
{
$(this).dialog('destroy');
verifyTOS_PVM_v2_callback = "Kyllä"
}
},
{
text:"Ei",
click:function()
{
$(this).dialog('destroy');
verifyTOS_PVM_v2_callback = "Ei"
}
}
]
jQueryPrompt('Message', 'Koskien päivämäärää...', 400, buttons);
while(verifyTOS_PVM_v2_callback != "Kyllä" && verifyTOS_PVM_v2_callback != "Ei")
{
setTimeout('i = i + 1', 50)
}
res = verifyTOS_PVM_v2_callback;
if(res == "Kyllä")
{
error_occured = 2;
error = 'Message'
}
else
{
error_occured = 1;
error = 'Message'
}
}
} else {
error_occurred = 1;
error = "Message";
}
reterr[0] = error_occurred;
reterr[1] = error;
return reterr;
}
Prompt function:
function jQueryPrompt(msg, title, width, buttons)
{
$('body').append('<div id="jQueryPromptHost"></div>');
$('#jQueryPromptHost').append(msg);
$('#jQueryPromptHost').dialog({
title: title,
resizable: false,
width: width,
daraggable: false,
modal: true,
buttons: buttons
})
}
I have tried polling for a variable and that failed miserably (firefox just hanged and took more memory for itself...)
Do you have any suggestions?
Regards,
Akke
EDIT:
I have picked another approach to this problem. I marked the closest solution as the answer, in case someone else picks his approach. Thank you all!
In your click event handler simply call a function instead of assigning a value.
buttons = [
{
text:"Kyllä",
click: function() {
$(this).dialog('destroy');
handleButtonClick("Kyllä");
//verifyTOS_PVM_v2_callback = "Kyllä"
}
},
{
text:"Ei",
click: function() {
$(this).dialog('destroy');
handleButtonClick("Ei");
//verifyTOS_PVM_v2_callback = "Ei"
}
}
]
//Somewhere else in the code
var handleButtonClick = function(value) {
if (value == "Kyllä") {
...
} else if (value == "Ei") {
...
}
};
while loop is locking, you can not use it.
Going to have to break up the function into two parts. First part is code before you call the dialog, second part is the part after the dialog. The dialog button clicks call the second function.
If the code has to be synchronous, you are sort of out of luck and stuck with the ugly window.prompt.

Is there a more elegant solution than an if-statement with no else clause?

In the following code, if Control (the element that trigers Toggle's first OL) is not Visible it should be set Visible and all other Controls (Controls[i]) so be Hidden.
.js
function Toggle(Control){
var Controls=document.getElementsByTagName("ol",document.getElementById("Quote_App"));
var Control=Control.getElementsByTagName("ol")[0];
if(Control.style.visibility!="visible"){
for(var i=0;i<Controls.length;i++){
if(Controls[i]!=Control){
Reveal("hide",20,0.3,Controls[i]);
}else{
Reveal("show",20,0.3,Control);
};
};
}else{
Reveal("hide",20,0.3,Control);
};
};
Although the function [Toggle] works fine, it is actually setting Controls[i] to Hidden even if it is already.
This is easily rectified by adding an If statement as in the code below, surely there is a more elegant solution, maybe a complex If condition?
.js
function Toggle(Control){
var Controls=document.getElementsByTagName("ol",document.getElementById("Quote_App"));
var Control=Control.getElementsByTagName("ol")[0];
if(Control.style.visibility!="visible"){
for(var i=0;i<Controls.length;i++){
if(Controls[i]!=Control){
if(Controls[i].style.visibility=="visible"){
Reveal("hide",20,0.3,Controls[i]);
};
}else{
Reveal("show",20,0.3,Control);
};
};
}else{
Reveal("hide",20,0.3,Control);
};
};
Your help is appreciated always.
In the ugly pure javascript code world, your solution is fine. But only because you said "elegant", my answer is use jQuery.
I'll write it probably closer to what it really would be, using behaviour-based code rather than event-based, so this won't EXACTLY match your code.. but, it would look something like:
$('#Quote_app ol').click(function() {
if ($(this).is(':visible')) {
$(this).fadeOut();
} else {
$(this).fadeIn();
$('ol', $(this).parent()).not(this).fadeOut();
}
});
That attaches a click event to every ol element underneath something with ID=Quote_app, and if it's currently visible, hides it, and otherwise, shows it, and hides all other ol elements.
if(Controls[i]!=Control && Controls[i].style.visibility=="visible") {
Reveal("hide",20,0.3,Controls[i]);
}
Not sure about what means what in your code. Stratagy is to do default action for all items first, and then do specifica action for selected item. Something like this:
for(var i=0;i<Controls.length;i++){
if(Controls[i].style.visibility=="visible"){
Reveal("hide",20,0.3,Controls[i]);
};
}
Reveal("show",20,0.3,Control);
if( Controls[i] != Control ) {
if( Controls[i].style.visibility == "visible" ){
Reveal( "hide", 20, 0.3, Controls[i] );
};
} else {
Reveal( "show", 20, 0.3, Control );
};
could be rewritten as:
if ( Controls[i] == Control ) {
Reveal( "show", 20, 0.3, Control );
} else if ( Controls[i].style.visibility == "visible" ) {
Reveal( "hide", 20, 0.3, Controls[i] );
}
To follow on from the jQuery suggestion -
jQuery often has a toggle function which beomes even more attractive in this situation as it reduces your code to a couple of lines. There currently isnt a toggleFade function but it can be easily added, to quote Karl Swedberg:
You can write a custom animation like this:
jQuery.fn.fadeToggle = function(speed, easing, callback) {
return this.animate({opacity: 'toggle'}, speed, easing, callback);
};
Then, you can do this:
$(".bio-toggler").click(function () {
$("#bio-form").fadeToggle();
})
;
This will work without you having to use getComputedStyle, assuming your Reveal("hide", ...) function sets visibility to hidden.
if(Controls [i] !== Control && Controls[i].style.visibility !== "hidden") {
Reveal("hide", 20, 0.3, Controls[i]);
}
With a little monkey-patching you could make this a lot cleaner without using any external framework. I have also taken the liberty to reshuffle the logic based on the assumption that the ordering of animations (if any) is unimportant.
if Control is hidden
loop through Controls as C
hide if C != Control
show if C = Control
else
hide Control
Another way to interpret this algorithm is - as long as Controls contains at least one element (doesn't matter which), the visibility of Control will be toggled. And all (Controls minus Control) will be hidden. So I'm again taking the liberty to assume that there will always be one control in Controls, and that Control will always be toggled.
Here's the monkey-patch++ code for it (also on jsfiddle). This eliminates all ifs and elses from the function.
The Toggle function now looks like this:
function Toggle(Control) {
var Controls = document.getElementsByTagName("ol" ..
var Control = Control.getElementsByTagName("ol")[0];
Control.toggle();
Controls.filter(function(c) {
return c != Control && c.isVisible();
}).hide();
};
Here is the code-behind. NodeList and Array that apply a property on a list of elements:
​NodeList.prototype.forEach = function(f) {
for(var i = 0; i <​ this.length; i++) {
f.apply(null, [this[i]]);
}
};
Array.prototype.forEach = NodeList.prototype.forEach;
NodeList.prototype.filter = function(f) {
var results = [];
for(var i = 0; i < this.length; i++) {
if(f.apply(null, [this[i]])) {
results.push(this[i]);
}
}
return results;
};
Array.prototype.filter = NodeList.prototype.filter;
NodeList.prototype.hide = function() {
this.forEach(function(e) {
e.hide();
});
};
Array.prototype.hide = NodeList.prototype.hide;
NodeList.prototype.show = function() {
this.forEach(function(e) {
e.show();
});
};
Array.prototype.show = NodeList.prototype.show;
These methods apply a property on an individual element:
Element.prototype.isVisible = function() {
return this.style.visibility == 'visible' || this.style.visibility == '';
};
Element.prototype.show = function() {
this.style.visibility = 'visible';
};
Element.prototype.hide = function() {
this.style.visibility = 'hidden';
};
Element.prototype.toggle = function() {
this.isVisible() ? this.hide() : this.show();
};

Categories