Animating brooms - javascript

In Javascript, when I click on the broom, it changes the image src to a gif and changes the ID to "walkingBroom". This works.
Then, when I click on the broom, it changes the class (this makes it walks across the screen). This does not work.
I don't know what I doing wrong - can anyone help? It's like the ID has not been changed.
document.getElementById("stillBroom").addEventListener("dblclick", animate);
function animate(){
document.getElementById("stillBroom").src = "broom.gif";
document.getElementById("stillBroom").id = "walkingBroom";}
document.getElementById("walkingBroom").addEventListener("click", walk);
function walk(){
document.getElementById("walkingBroom").className = "broom pattern0 speed1";}

You should manage the logic in a variable, like let currentState="still", then on click, change to currentState="walking", and act in consequence. Currently, you are immediately trying to attach a click event to a "walkingBroom" element that doesn't exist (it will exist, but later, after you click the broom once). For that matter, your console is very likely saying Error - Can't read property addEventListener of null and your script crashes without running. Open your console, it's very useful.
let currentState = "still";
broom.addEventListener("dblclick", clickHandler);
const clickHandler = () => {
switch(currentState) {
case "still" :
currentState="walking";
broom.src="broom.gif";
break;
case "walking":
currentState="flying";
broom.className = "broom pattern0 speed1";
break;
}
}

Related

Removing Div from Website only works the first time

I am trying to build an chrome extension for lichess.org to permanently remove some elements from the website.
As the elements (in this case divs) can reappear if the user navigates through the website, I implemented a MutationObserver to remove the divs again as soon as they get added again. However, even though the function to remove them is called and they don't change their data-id, they are only removed when the function is called for the first time.
This is what I've tried so far:
These are the divs I want to remove. They are exactly the same after reappearing.
const bullet1 = document.querySelector('[data-id="1+0"]');
const bullet2 = document.querySelector('[data-id="2+1"]');
This is the MutationObserver. The div gets added with the addedNode.
const parent_lobby = document.querySelector("#main-wrap > main");
const mutationObserver = new MutationObserver(mutations => {
if (mutations[0].addedNodes[0].className == "lobby__app lobby__app-pools"){
remove_bullet_QP();
}
})
mutationObserver.observe(parent_lobby, {childList: true})
This is the function called to remove the elements. The first call of the method that happens as soon a the webiste is opened.
function remove_bullet_QP(){
bullet1.remove();
bullet2.remove();
}
remove_bullet_QP();
I've also tried to overwrite the divs before calling the function to remove them, but it didn't change the result.
Thank you for your help.
I changed bullet1 and bullet2 from const to let and reassigned them in every function call. This seems to work.
As I've said in my question, I've tried reassigning before, but without changing it to let. This should have given me an error message, somehow it didn't.

.addEventListener vs .onclick

I keep reading that there is no difference between the two but I am writing a quiz app and am seeing some differences
For example:
Quiz.prototype.handleGuess = function (id, guess) {
let button = document.getElementById(id);
button.addEventListener('click', function(){
quiz.guess(guess);
console.log(guess);
}
if(!quiz.hasEnded){
quiz.displayNext();
} else {
quiz.displayScore();
};
};
When using an event listener, I will log the guess to the console on the first button choice. When I choose an answer for the second question the console will read not only the new selection but also the choice I made from the previous question. This does not happen when using .onclick() and I am not sure why!
Consider the following code:
var el1 = document.getElementById("someEl1"),
el2 = document.getElementById("someEl2");
function firstHandler() {
alert("First handler");
}
function secondHandler() {
alert("Second handler");
}
el1.addEventListener("click", firstHandler);
el1.addEventListener("click", secondHandler);
el2.onclick = firstHandler;
el2.onclick = secondHandler;
<div id="someEl1">First Element</div>
<div id="someEl2">Second Element</div>
In case 1, clicking on el1 will alert with both messages. In case 2, clicking on el2 will only alert with the second because we overwrote what onclick does with the second assignment.
addEventListener effectively assigns a callback to some internal array of listener callbacks that will all be called whenever the event is triggered.
onclick is a single property with a single value. When you assign to it, the old value is replaced by the new assignment.
I would highly suggest that you do not use the onclick method. It makes code harder to maintain. If you are in a large code base and you set the onclick of an element and then later on another coder also sets the onclick without knowing that that element already had its onclick set, then you will run into a difficult time trying to figure out why your code is broken all of a sudden. Using the event listener pattern makes for more extensible and decoupled code.

How to get URL of an image in JavaScript?

I'm developing a Chrome extension, and I'm adding an onmouseover handler to each of the images on a page. When the user mouses over an image, it's URL should be stored in a variable. I know I can easily get the value of the src attribute of the image, but I want the full URL. The src attribute stores the path of the image on the server. For example, when you right click an image in Google Chrome, you get the "Copy Image URL" option, which copies the image's URL to the clipboard.
Is there any way to achieve this? Thanks.
Instead of imageElement.getAttribute("src") or $("img.something").attr("src"), which reads the original markup, use imageElement.src property which will always give you the full URL.
var imgFullURL = document.querySelector('img.something').src;
or:
var imgFullURL = $('img.something')[0].src;
To extract host name, path name etc. - parse the url with URL() constructor, which works in modern browsers or use the legacy method via creating a temporary a node.
You can use window.location to get the page you are currently on and the following will give you the URL parts you need:
window.location.protocol = "http:"
window.location.host = "stackoverflow.com"
window.location.pathname = "/questions/32828681/how-to-get-url-of-an-image-in-javascript"
So, likely, you will need protocol, then "//", then host and finally the image src.
So the TL;DR is this:
(function() {
const imageInfo = new Object();
imageInfo.source = '';
window.addEventListener('mouseover', function (event) {
var currentElement = event.target;
// console.log(event.target);
if (currentElement.tagName === 'IMG') {
// console.log(currentElement.outerHTML + "is a photo");
imageInfo.source = currentElement.src;
// console.log("src is :" + imageInfo.source)
return imageInfo.source;
}
})
})();
See CodePen:
How to find the src URL for a photo by Trevor Rapp on
CodePen
This is how I thought about solving the problem in the most basic steps:
get the function to fire.
get the function to add an event listener that will perform an action on a mouseover event.
make that action know what the mouse is currently over.
figure out if what the mouse is currently over is an image or not.
create logic that will respond if it is.
that action that logic should do is return the source URL.
I will need to store that source URL if I am going to have to return it.
Here are how each of those solutions looked:
get the function to fire.
An IFFE is a great way to get a function to fire without having to worry about polluting the name space.
//skeleton for an IFFE statement
(function() {
})();
get the function to add an event listener that will perform an action on a mouseover event.
An event listener that could fire anywhere would have to be attached to the window or the document.
make that action know what the mouse is currently over.
This part will be combined with part 2. Event listener's first parameter is what type of event you want to listen for -- in this case 'mouseover. So now our code looks like this
(function () {
window.addEventListener('mouseover', function (event) {
//do stuff here
}
})()
figure out if what the mouse is currently over is an image or not.
*To figure out which element the mouse if currently over you would use Event.target.
The MDN definition for that is: *
The target property of the Event interface is a reference to the object onto which the event was dispatched. It is different from Event.currentTarget when the event handler is called during the bubbling or capturing phase of the event. --Event.Target
*So the code would then look like this: *
(function () {
window.addEventListener('mouseover', function (event) {
//get the current element the mouse is over
var currentElement = event.target;
}
})()
create logic that will respond if it is.
This was a little trickier since a photo or IMG can be presented in various ways.
I chose to create a solution for the simplest way, which is assuming that the web developer used the more syntactically correct version of an tag. However, there are many times when they may choose to apply a 'background-image' CSS property to a normal . Other things to consider could be the use of iframes, which can make detecting the attributes of child elements very frustrating since they don't allow bubbling to occur. To tell if an element is an , you can simply use elem.tagName === "IMG" for your logic check. While not included in the above code, if you wanted to check if a div is using the 'background-image', you could use something like element.getAttribute('style').includes('term') and switch out 'term' for something like 'url' or 'jpg' or 'png.' Kind of clunky and hacky, but just a thought. Anyway, the code would then become
(function () {
window.addEventListener('mouseover', function (event) {
//get the current element the mouse is over
var currentElement = event.target;
if (currentElement.tagName === 'IMG') {
//do stuff
}
}
})()
that action that logic should do is return the source URL.
Once you get the logic done and you have properly selected the element, then you can use element.src to get the source URL.
I will need to store that source URL if I am going to have to return it.
You can do this anyway you want, but I played around with instantiating an object since it sounded like the value would need to change often, but you didn't necessarily need to store previous values.
And so the final product could be something like this
(function() {
const imageInfo = new Object();
imageInfo.source = '';
window.addEventListener('mouseover', function (event) {
var currentElement = event.target;
// console.log(event.target);
if (currentElement.tagName === 'IMG') {
// console.log(currentElement.outerHTML + "is a photo");
imageInfo.source = currentElement.src;
// console.log("src is :" + imageInfo.source)
return imageInfo.source;
}
})
})();

Simple onClick Every Other Click Javascript HTML

onclick="HandleAction(\'playnow\');HandleAction(\'stop\');"
Performs the two functions simultaneously. No good since it just does the action \playnow\ and then \stop\ immediately after.
I need onClick to perform the function every other click.
Click 1: \playnow\
Click 2: \stop\
Click 3: \playnow\
etc.
Is there a simple way to achieve this?
Define a var which holds the current state:
var state = false;
function handleAction() {
if (state == false) {
// stuff for 'playnow' action
state = true;
return;
}
if (state == true) {
// stuff for 'stop' action
state = false;
return;
}
}
Declare a global variable and interchange it depending on what is passed:
var switch = "playnow";
function handleAction(a) {
if a === switch {
alert(a);
}
else {
alert(a);
switch = a;
}
}
See my answer to this question.
Lets make a function called toggle that lets you specify actions that happen every other click
var toggle = function (a, b) {
var togg = false;
return function () {
// passes return value back to caller
return (togg = !togg) ? a() : b();
};
};
We would then setup our click handler like this
button.addEventListener('click', toggle(function () {
//play now logic
}, function () {
// stop logic
}));
Your click handler now alternates between the first and second functions with every click.
Yes. You could do something like
var play=false,
button = document.getElementById('playbtn'),
audiotag = document.getElementById('audio');
button.addEventListener('click', function(){
if(play){
audiotag.pause()
play=true;
}else{
audiotag.play();
play=false;
}
})
to make this work you could use html like this:
<div id="audioplayer">
<button id="playbtn">play</button>
<audio id="audiotag" src="path/to/audiofile"></audio>
</div>
so you would add the audio html like above and use getElementById to get each element in javascript. after that you attach an event listener to listen for the click event and call the handler which is the anonymous function. Inside that function, you can use the native play and pause methods directly on the audio object to stop audio when it's playing and then play it again when it's stopped.
there are other attributes you can add to the audio tag to start it playing as soon as the page loads. When you click the button, the play variable is set to true so it will pause on the first click and then sets that to false. A subsequent click will play it again and set the variable to true again and so on
Tyr gave you an answer how to solve your problem. Here you go some notes which can help you design better code.
If you have i.e. one big animation and one button on your web, your code is perfectly ok, to keep the code simple is a good idea. But if you have something like this
<button onclick="...">Animation A</button>
<button onclick="...">Animation B</button>
Then you need better design. If you insert state global var into HandleAction, you break low coupling, HandleAction is bound to your single event and can't be reused elsewhere.
It is good to ask yourself What does this function do? In the first case, it is useful to choose better name, like HandleIntroAnimation. If it handles (any) animation, then it is good to specify it in the parameter.
function HandleAnimation(animation, play) {
if(play) ... // playnow
else ... // stop
}
This indeed does what the name tells. To use it in your code, write a proxy function:
<script>
// If you enhance your web, you only alter this code.
// HandleAnimation stays the same, regardless the changes.
var animationStatus = {}
function ToggleAnimation(animation) {
animationStatus[animation] = !animationStatus[animation];
HandleAnimation(animation, animationStatus[animation]);
}
</script>
<button onclick="toggleAnimation(this)">Animation A</button>
<button onclick="toggleAnimation(this)">Animation B</button>
Finally, you could completely decouple HTML and JS:
animations.js
window.addEventListener("load",function() {
// HandleAnimation and ToggleAnimation definitions goes here
// to avoid collisions in global namespace
var anims = document.getElementsByClassName("animation");
for(var i=0; i<anims.length; ++i) anims[i].addEventListener("click",function() {
ToggleAnimation(anims[i]);
});
});
your html
<script src="animations.js"></script>
<button class="animation">Animation A</button>
<button class="animation">Animation B</button>
and you have animation framework: every element with animation class magically toggles its animation. The animation data could be provided in data-* attribute, data-animation in this case.
Then you can provide it as open-source on github or use someone elses open code to fill the missing parts in your code where you were too lazy to code it yourself. Since many wheels were already invented, the only thing you need to code is usually proxy functions. That's how coders save each others time. Happy coding.

Javascript event firing before action occurs

I am trying to write a script so that when I play an embedded sound object, a picture that I also have embedded will change.
function changePic() {
document.getElementById("sound").onclick = transform(document.getElementById("pic"));
}
function transform (pic) {
pic.src = "";
alert ("done");
}
The problem is that when I load the page, the Javascript code automatically runs even though I don't click play (autostart is set to false) on the sound object. Does anyone have an idea as to what is causing this?
When you write onclick = transform(...), you're calling transform and assigning the result to onclick.
You need to set the handler to an anonymous function that calls transform, like this:
document.getElementById("sound").onclick = function() {
transform(document.getElementById("pic"));
};
However, this is the wrong way to add events.
You should call element.addEventListener / element.attachEvent. (or just use jQuery)

Categories

Resources