I'm still in the process of learning JavaScript. and I would like to complete the task using only JavaScript and no Jquery.
I have multiple div/images that I’m trying to manipulate using the z-index, and a button that randomize the images to come to the front.
I got the random image array to work but as you could see in image[1]…setting each changeZ index will be laborious. So I’m embarking on changing the class’s (as seen in image[0] so I could add current to the new image and send current to the background on the next go around and then removing the class attribute. I have got the element to work separate but having trouble putting it together in a array.
function changeZIndex(i,id) {
document.getElementById(id).style.zIndex=i;};
function changeClassZIndex(i,tagName){
document.getElementsByTagName("*").style.zIndex=i;};
function getImage(){
var whichImage=Math.floor(Math.random()*3);
var image=new Array()
var currentPhoto = div.current
image[0]=function() {
changeZIndex(5,"scene1");
changeClassZIndex(-5,"current");
currentPhoto.removeClass('current')
document.getElementById("scene1").className += "current"; };
image[1]=function() {
changeZIndex(5,"scene2");
changeZIndex(-5,"scene1");
changeZIndex(-5,"scene3");
changeZIndex(-5,"scene");
};
image[2]=function() {
changeZIndex(5,"scene3");
changeZIndex(-5,"scene");
changeZIndex(-5,"scene2");
changeZIndex(-5,"scene1");
};
image[whichImage].apply(undefined);};
It's because document.getElementsByTagName() returns an array of elements, which you can't do operations like that on. Instead, you need to enumerate through them and do the operations individually.
Here's a working jsfiddle which shows exactly how to do it: jsfiddle
As a side note: if there's one thing a lot of web programming will teach you, its this:
Dont ever, ever, rule out jQuery as an option.
JQuery is your best friend, and the use of it in this situation would cut down your lines of code by well over half.
Firstly, I believe your problem is probably in changeClassZIndex(i,tagName)
which should probably look something like this:
if (document.getElementsByClassName == undefined) {
document.getElementsByClassName = function(className)
{
var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)");
var allElements = document.getElementsByTagName("*");
var results = [];
var element;
for (var i = 0; (element = allElements[i]) != null; i++) {
var elementClass = element.className;
if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass))
results.push(element);
}
return results;
}
}
function changeClassZIndex(z,className) {
var e = document.getElementsByClassName(className);
for(var i = 0; i < e.length; i++) {
e[i].style.zIndex = z;
}
};
I am defining the getElementsByClassName function if it does not exist because some browsers may not support it.
I may suggest taking a different approach to your problem however:
var images = new Array("scene1", "scene2", "scene3");
var currentPhoto = div.current
var whichImage = Math.floor(Math.random()*images.length);
// change all images to the background
for(var i = 0; i < images.length; i++)
{
changeZIndex(-5, images[i]);
}
// change the one you want to the top
changeZIndex(5, images[whichImage]);
That way you do not have to write functions for each image, and adding images is as easy as adding to the array.
Related
Im working on a RPG-game project made in plain javascript just for fun, but can't seem to understand how javascript build-in classes work since they behave quite a lot differently than I am used to in Java or C#. So here is the problem:
If I have made custom class something along the lines like this:
Class Player_animation{
constructor(animationX,animationY,animation_state,xPos,yPos,destX,destY){
this.animationX = animationX;
.
. (the basic setup for all the attributes as the first one);
.
set animationX(value){
this._animationX = value;
}
//all the setters as that one
update(){
if(this._animationX===480 && this._animation_state==='idle')
this._animationX=0;
else if(this._animationX===720 && this._animation_state !== 'attack'){
this._animationX=0;
}
else if(this._animationX===840){
this._animationX=0;
this._animationY = 0;
this._animation_state = 'idle';
}
if(this._xPos!== this._destX || this._yPos!== this._destY){
if(this._xPos<this._destX){
this._animation_state = 'facing_right';
this._animationY = 240;
}
if(this._xPos>this._destX){
this._animation_state = 'facing_left';
this._animationY = 360;
}
}
else{
if(this._animation_state === 'facing_right')
this._animationY = 0;
if(this._animation_state === 'facing_left')
this._animationY = 120;
if(this._animation_state!=='attack'){
this._animation_state = 'idle';
}
}
}
}
And i can call an new made class object no problem in my program like this:
var player_animation = new Player_animation(0,0,'idle',0,0,0,0);
player_animation.update();
Can I somehow make an array of these custom classes that I call with that function. I have tried two of the following approaches like this:
var array = [];
array.push[player_animation,player_animation2];
for(var unit in array){
unit.update();
}
Second approach i tried that does not work:
var array = [];
array.push[player_animation,player_animation2];
for(var i = 0; i < array.Lenght; i++){
array[i].update();
}
Working code goes through this loop (I know that I should limit the fps here):
function frame(){
update();
requestAnimationFrame(frame);
}
function update(){
player_animation.update();
enemy_animation.update();
}
requestAnimationFrame(frame);
Is something like this even possible in plain javascript?
My game loop and update work fine with all of the objects defined called separately but that will be a hassle after there will be 10+ objects in game.
In Java i was able to store all of the game_objects in an array where their functions would be called through a for loop so finding it hard to understand why it does not work as that in javascript.
You have many problems, none of them relating to how classes work in JavaScript.
array.push[player_animation,player_animation2];
push is a function. You call it with () not [].
for(var unit in array){
in loops over the property names in an object. Avoid using it for arrays. If you do use it, then you would need array[unit] to get the value and not simply unit.
for(var i = 0; i < array.Lenght; i++){
JavaScript is case-sensitive
JavaScript requires that property names be spelt correctly
It is length not Lenght.
List only rendered styles, not arbitrary ones that aren't applied
I've tried many things to get the styles applied to an element but have come up blank.
Please do not cite getComputedStyle as being a solution unless you can solve the junk returns issue.
The primary problem is that window.getComputedStyle(document.querySelector('ANY ELEMENT')).fill will return "rgb(0, 0, 0)", which is not the correct style in almost any instances, and has no apparent way to destinguish if its actually being applied or not.
The above example is not the only problem case; there are tons of rules returned by getComputedStyle which are wrong and will drastically change the look of the page if they are applied.
Static parsing is not an option as there are cases where the .css files are on another server with no cross-origin headers; which also hides styles usually found in document.styleSheets.
Is there any way to get a list of the applied styles and nothing else?
As requested this code will demonstrate the problem (on Chrome):
var all = document.getElementsByTagName('*');
for(var i in all)
if (all[i].style) all[i].style.cssText = window.getComputedStyle(all[i]).cssText;
EDIT: My answer has code which works on all browsers. I keep above to preserve comment thread.
Here are the version that don't need to check depth.
The problem in your code is the assign of inline style in the previous element will affect the getComputedStyle result of the next result. It mean the value of getComputedStyle is always changing in the loop. You can first store it in an array like this.
var all = document.getElementsByTagName('*');
tmpArr = []
for(var i in all) {
if (all[i].style) {
tmpArr[i] = window.getComputedStyle(all[i]).cssText;
}
}
for(var i in all) {
if (all[i].style) {
all[i].style.cssText = tmpArr[i]; ;
}
}
console.log("finish");
You can change tmpArr[i] = window.getComputedStyle(all[i]).cssText; to tmpArr[i] = window.getComputedStyle(all[i]).cssText + "-webkit-text-fill-color:#691099!important"; to test whether it work
It will be slow if you open the inspector since there are too much inline style, but it will solve the problem if all you need is just put the style to be inline style.
Partial Answer (Updated):
It is possible to get only the active styles by calling my function getRenderedStyles:
getRenderedStyles now bypasses active stylesheets for more accurate output.
function getRenderedStyles(element) {
var tmpele, tmpstyle, elestyle, varstyle, elecolor, eletag;
var styles = {};
var defstyle = {};
elestyle = window.getComputedStyle(element);
elecolor = elestyle.color;
eletag = element.tagName;
var frag = document.createDocumentFragment();
frag.appendChild(document.documentElement);
tmpele = document.appendChild(document.createElement(eletag));
tmpstyle = window.getComputedStyle(tmpele);
styles['color'] = elecolor===tmpstyle.color?undefined:elecolor;
tmpele.style.color = elecolor; // workaround for color propagation on other styles
for (var i in tmpstyle)
defstyle[i] = tmpstyle[i];
tmpele.remove();
document.appendChild(frag);
varstyle = element.style;
for (var i in varstyle) {
if ((((typeof varstyle[i])==="string"))&&(i!=="cssText")) {
if ((defstyle[i]!==elestyle[i]))
styles[i] = elestyle[i];
}
}
return styles;
}
Sadly there's a caviat as the browser still seemingly returns invalid styles in some cases. Often shifting the locations of elements.
To verify this you may run the following code, which takes into account parent/child inheritance, in an attempt to properly apply the current styles to the page:
function DOMDepth(element) {
var cur = element;
var deep = 0;
while(cur.parentNode)
deep++, cur = cur.parentNode;
return deep;
}
function getElementsByDepth() {
var all = document.getElementsByTagName('*');
var depth_map = {};
var deepest = 0;
for(var i in all) {
var depth = DOMDepth(all[i]);
deepest = depth>deepest?depth:deepest;
depth_map[depth] = depth_map[depth] || [];
depth_map[depth].push(all[i]);
}
depth_map['deepest'] = deepest;
return depth_map;
}
function inlineComputedStyles() {
var depth_map = getElementsByDepth();
for (var i = depth_map.deepest; i>0; i--) {
var elements = depth_map[i];
for (var j in elements) {
var styles = getRenderedStyles(elements[j]);
for (var k in styles) {
elements[j].style[k] = styles[k];
}
}
}
}
I have tested the preceeding and can confirm it does not suffer the color problems of the snippet in the question. Sadly I am uncertain as to why some elements still shift or if there's a way to fix it.
Special thanks to Kit Fung for pointing out the inheritance problem.
I'm trying to give each div a different background colour. Here is my current code:
http://jsfiddle.net/Uy2FX/2/
var imgColours = ['#FCCF94', '#C4C9E5', '#ADE3D6'];
for (i=0; i < imgColours; i++) {
$('.img').css({backgroundColor: imgColours[0]});
}
However, I'm not quite sure where this is going wrong. I understand that's probably too simple to work, but in my mind it makes sense. Could someone point me in the right direction?
There are some relevant errors in your code.
This is probably what you wanted to do:
// V1 : Basic
var imgColours = ['#FCCF94', '#C4C9E5', '#ADE3D6'];
for (var i=0; i < imgColours.length; i++) {
$('.img:eq('+i+')').css({backgroundColor: imgColours[i]});
}
But if you want to get a random color from your array, for any number of divs, and also optimise your jQuery code a bit for better performance:
// V2 : random colors
var $imgs = $('#boxes1').find('.box'),
imgsCount = $imgs.length,
coloursCount = imgColours.length;
for (var i=0; i < imgsCount; i++) {
var rnd = Math.floor(Math.random() * coloursCount),
color = imgColours[rnd];
$imgs.eq(i).css({backgroundColor: color});
}
Or, if you want to loop through the colours following the order of the array, just change the loop:
// V3 : sequential colors
// Add V2 variables here
for (var i=0; i < imgsCount; i++) {
var color = imgColours[i%coloursCount];
$imgs.eq(i).css({backgroundColor: color});
}
UPDATED FIDDLE: http://jsfiddle.net/Uy2FX/12/
For some very basic tips on jQuery selectors performance: http://www.sitepoint.com/efficient-jquery-selectors/
You are always assigning imgColours[0] to EVERY div. I think what you are looking for is imgColours[i]
You will also need to use imgColours.length to tell your loop how long the array is.
You are also grabbing all HTML elements with the class of img, so this will change all of them each time.
To grab each element separately, you can use the CSS nth-of-type selector. Basically you can just do something like
$(".img:nth-of-type(" + i + ")")
You need to use imgColours.length
The for loop has no idea how long the array is otherwise
Edit: What's the point in this for loop if you end up using imgColours[0] anyways? If you want to loop each color, use i instead of 0.
And either way, this will not achieve a different background per div.
Try selecting by className (I'm going to use vanilla.js because it's simple)
var elements = document.getElementsByClassName("img");
for (var i = 0; i<elements.length; i++) {
var color = imgColours[Math.floor(Math.random()*imgColours.length)]; //get a RANDOM color change me if needed
elements[i].style.backgroundColor = color;
}
How about this?
var ec = 0;
var i = 0;
for(ec; ec < elements.length; ec++, i++) {
elements[ec].style.backgroundColor = imgColours[i];
if(i == (imgColours.length - 1)) i = -1;
}
http://jsfiddle.net/y2dq3/
My code should insert HTML content in all divs that have a predefined class name, without using jQuery and at least compatible with IE8 (so no getElementsbyClass).
The html:
<div class="target">1</div>
<div class="target">2</div>
<div class="target">3</div>
<div class="target">4</div>
The javascript:
var elems = document.getElementsByTagName('*'), i;
for (wwi in elems) {
if((' ' + elems[wwi].className + ' ').indexOf(' ' + "target" + ' ') > -1) {
elems[wwi].innerHTML = "YES";
//elems[wwi].innerHTML = "<div>YES!</div>";
}
}
You can try it here.
As you can see inside each div the word YES is printed. Well the if you comment elems[wwi].innerHTML = "YES"; and replace that for elems[wwi].innerHTML = "<div>YES!</div>" the code fails. I suppose is because inserting div elements modify the DOM and in consequence the FOR cycle fails. Am i right?
Well i can solve this pretty ugly by recalling the for cycle each time i make an innerHTML, and when i insert the code i can add a class (like data-codeAlreadyInserted=1) to ignore the next time the FOR pass in that div. But again, this is pretty much a very bad solution since for an average site with many tags I can even freeze the user browser.
What do you think? lets suppose i dont know the amount of tags i insert on each innerHTML call.
"I suppose is because inserting div elements modify the DOM and in consequence the FOR cycle fails. Am i right?"
Pretty much. Your elems list is a live list that is updated when the DOM changes. Because you're adding a new div on every iteration, the list keeps growing and so you never get to the end.
To avoid this, you can either do a reverse iteration,
for (var i = elems.length-1; i > -1; i--) {
// your code
}
or convert the list to an Array.
var arr = [];
for (var i = 0, len = list.length; i < len; i++) {
arr.push(elems[i]);
}
for (i = 0; i < len; i++) {
// your code
}
Another way is to use replaceChild instead of innerHTML. It works better and it's way faster:
var newEl = elem[wwi].cloneNode(false);
newEl.innerHTML = html;
elem[wwi].parentNode.replaceChild(newEl, elem[wwi]);
You can take a copy of the live node list:
var nodes = [];
for (var i = 0, n = elems.length; i < n; ++i) {
nodes.push(elems[i]);
}
and then use a proper for loop, not for ... in to iterate over the array:
for (var i = 0, n = nodes.length; i < n; ++i) {
...
}
for ... in should only be used on objects, not arrays.
How can I replace a text or number in a span element with javascript/jquery?
this is what i have tried but the console says "Cannot call method 'replace' of undefined" dont understand whats the problem, can someone point me in the right direction?
function changeNumber()
{
var element = document.getElementsByClassName("grade").innerHTML;
var x = element.replace('1','*');
document.getElementsByClassName("grade").innerHTML=x;
}
If it matters the span element i created dynamically in javascript...
document.getElementsByClassName("grade") returns a NodeList, it is not like getElementbyId, you need to loop it.
If you just have one
function changeNumber()
{
var element= document.getElementsByClassName("grade")[0];
var text = element.innerHTML;
var x = text.replace('1','*');
element.innerHTML=x;
}
If you have a set
function changeNumber()
{
var elements= document.getElementsByClassName("grade");
for (var i=0; i<elements.length; i++) {
var element = elements[i];
var text = element.innerHTML;
var x = text.replace('1','*');
element.innerHTML=x;
}
}
If you already have jQuery included in your page, and are comfortable with it's usage, try the following code:
function changeNumber()
{
$(".grade").each(function(){
var self = this
self.text(self.text().replace('1','*'))
});
}
Working demo: http://jsfiddle.net/pratik136/uUHv8/
While the posted answers are correct, and despite the fact that you've accepted an answer, I was intrigued by the possibility of creating a replace() function to work with a nodeList (or, strictly-speaking, an Object), so:
Object.prototype.replace = function (n, r) {
var textMethod = this[0] && this[0].textContent !== undefined;
for (var i = 0, len = this.length; i < len; i++) {
if (textMethod) {
this[i].textContent = this[i].textContent.replace(n, r);
} else {
this[i].innerText = this[i].innerText.replace(n, r);
}
}
return this;
};
document.getElementsByTagName('p').replace(/1/, '*');
JS Fiddle demo.
I can confirm this works in Chrome 25, Firefox 19 (also 14 and 18, hadn't updated this computer in a while) under Win XP. I can't test in IE, since it, and JS Fiddle, seem to be having issues.
It may, also, not be a good idea; but it seemed useful (and relatively interesting) at the point at which I put it together. Hopefully it may be of some use.