Hopefully I can articulate this problem well. I'm working on a simple audio player in Titanium Desktop. The primary code I'm focused on right now is the following:
function pickMusicFolder (){
var win = Titanium.UI.getCurrentWindow();
win.openFolderChooserDialog(function(folderResponse) {
var file = Titanium.Filesystem.getFile(folderResponse[0]);
var listing = file.getDirectoryListing();
for (var i = 0; i < listing.length; i++) {
if (listing[i].isDirectory()) {
// if the listing is a directory, skip over it
$('#main').append('DIRECTORY: ' + listing[i].nativePath() +
'<br />');
// continue;
}
else {
// otherwise, print the filename of the file to the #main content window
var songOnList = listing[i].nativePath();
var songURL = songOnList.replace(/\\/g,"/");
$('#main ul').append('<li>' + songURL + '</li>');
}
}
});
};
function playSong(songURL){
var currentSong = Titanium.Media.createSound(songURL);
currentSong.play();
this.stopPlayback = stopPlayback;
function stopPlayback(currentSong){
currentSong.stop();
}
}
And then related HTML:
<input type="image" src="img/player_stop.png" name="stopPlayback" onClick="playSong.stopPlayback(songURL)" />
<input type="image" src="img/folder_add.png" name="pickMusicFolder" onClick="pickMusicFolder()" />
Now, both pickMusicFolder and playSong itself work properly. However, stopPlayback isn't working, I'm having a really hard time grasping how to deal with different functions for playing and stopping audio, since the code that's generating the clickable links is completely compartmentalized within the pickMusicFolder function, while the code for stopping playback is only attached to the one separate interface button.
I simply need access to the songURL variable between multiple functions in order to be able to perform operations on one solitary song while it's playing (or not). I'm avoiding resorting to global variables, as I find that to be sort of a cop out.
Anyone have any ideas? Any tips are much appreciated! (Oh, and please ignore my ugly code; was trying a bunch of hack-y solutions before posting.)
Very quickly solution is to store currentSong as property of function playSong:
function playSong(songURL){
var currentSong = Titanium.Media.createSound(songURL);
playSong.currentSong = currentSong; // store curernt playing song as property of function
currentSong.play();
}
playSong.stopPlayback = function() {
if (playSong.currentSong) {
playSong.currentSong.stop();
delete playSong.currentSong;
}
};
when song will be stopped just remove this property that would mean no song are playing now
You should use a closure. See below
var stopPlayback = function() { /* Placeholder, defined later */ };
var playSong = function(songURL){
var currentSong = Titanium.Media.createSound(songURL);
currentSong.play();
// Redefine stopSong in the parent scope
stopPlayback = function(){
// currentSong is available to this function via closure
currentSong.stop();
}
}
HTML Edit
<input type="image" src="img/player_stop.png" name="stopPlayback" onClick="stopPlayback()" />
As a side note, you should not be using HTML attributes to attach JavaScript events. This is a great series on event attachment, but what you really should read is this part. Also, every JS library provides methods for attaching events to make your life easier.
Related
I am just getting started on learning to code and am trying to create a game of checkers with what I have learned. After learning to back up my original attempt the hard way, I have restarted and am applying what I have learned since my original attempt by creating my board and checker pieces dynamically with JS.
The problem I am running into this time around is when it comes to adding eventListeners to my checkers. I have successfully gotten them to function as buttons and can get the assigned function to run when i click the button (although it runs every time I run the code as well), but I want to have the button return it's own ID when clicked so that I can use it in the functions that will move the checker pieces. I'm probably in a little over my head as I've only been learning for a couple of weeks but I don't want to give up on the dynamically created pieces just yet. Any help is appreciated. Here are my functions that I've written (any advice is welcome):`
function addElement(parent, newTag, newId, html, newClass){
var p = document.getElementById(parent);
var newElement = document.createElement(newTag);
newElement.setAttribute('id', newId);
newElement.innerHTML = html
newElement.setAttribute('class', newClass)
p.appendChild(newElement);
}
function addOnclick(checkerId, func){
var checker = document.getElementById(checkerId);
checker.onclick = func;
checker.addEventListener("onClick", func);
}
function returnId(id){
console.log(id);
}'''
This is where I attach the event to each piece:
for( i = 10; i<90; i++){
if (i % 10 === 0 || i % 10 === 9){addElement("board", "div", "s"+i, "X", "dummySquare")
} //select non "dummy" squares//
else{
var button = 0;
var x = "s"+i;
addElement("board", "div", "s"+i, "", "square" );
if(startingPosition.indexOf(x)>-1){
if (i<50){
addElement("s"+i, "button", "b"+i, "x", "checker")
document.getElementById("b"+i).classList.add("redChecker")
};
if (i>50){
addElement("s"+i, "button", "b"+i, "x", "checker")
document.getElementById("b"+i).classList.add("blackChecker")
};
// starting checkers added//
addOnclick("b"+i, returnId)
EDIT
After getting the onclick event to work, I am wanting to return the id property of the button in question: my thoughts were to try to call target the id property directly, then return it (log it just to make sure that it is functional first) but my approach is not doing the trick. It seems that the id argument is null and I'm not sure why.
function returnId(id){
element = document.getElementById(id);
elementId = element.id;
console.log(elementId)
}
I am working on a dialog script in Vanilla JS. I ran into a problem with the click event on the video image. Even tough the image is surrounded with an anchor tag it shows the image as the event.target on the "trigger-dialog-open" event.
Here is the HMTL:
<a class="trigger-dialog--open thumbnail" data-dialog-id="dialog-video" href="javascript:;">
<figure>
<img src="http://i.ytimg.com/vi/id/sddefault.jpg" alt="" />
</figure>
</a>
And this is the event in JS:
var openTriggers = document.getElementsByClassName('trigger-dialog--open');
for (var i = 0; i < openTriggers.length; i++) {
openTriggers[i].addEventListener("click", function (event) {
this.openDialog(event.target.getAttribute('data-dialog-id'));
}.bind(this), false);
}
The event handler wants to know the dialog-id from the anchors data attribute. It can't be found because it thinks the image is the event.target, not the actual anchor. How can I correct this? Thanks!
Use event.currentTarget. The event.target is supposed to be the img element since that is what the user has clicked on. The click then bubbles up through the image's containers. event.currentTarget gives you the element that the click handler was actually bound to.
(Or if you didn't bind this to some other object you could use this within the click handler and it should also be the current target.)
I have a few questions is the var openTriggers supposed to be a part of a module hash? Because if it's global then you don't use a this, you only add a this, if it's referencing a variable that the function is also contained in. For example:
var aThing = {
openTriggers: document.getElementsByClassName('trigger-dialog--open'),
openModal: null,
openDialog: function(clickedThingAttr){
if(this.openModal !== null){
this.openModal.style.display = 'none';
}else{
this.openModal = document.getElementById(clickedThingAttr);
}
this.openModal = document.getElementById(clickedThingAttr);
this.openModal.style.display = 'block';
},
setEventListenersNStuff: function(){
for (var i = 0, n = this.openTriggers.length;i < n; i++) {
this.openTriggers[i].addEventListener("click", function (event) {
this.openDialog(event.target.getAttribute('data-dialog-id'));
});
};
}
};//end of aThing hash object
aThing.setEventListenersNStuff();
There are a few issues here:
1. why are you using .bind I think that is a jQuery thing, you want to pass a string to another function when an object is clicked, there no need for binding at all.
2. Also make sure that if you want to do something like open a modal, there is no need to call another method unless it's kinda complex.
3. What about other potential dialogs, it seems that when a .trigger-dialog--open is clicked you're just showing that one one modal with the embedded id, but what about others? Make sure all modals are closed before you open a new one, unless you want to have like 10 modals are open.
A thing to note: I added the line var i = 0, n = openTriggers.length;i < n; i++, now in this case it's silly optimization, and I heard for modern browsers this doesn't apply, but to explain why I added it, is because i < openTriggers.length would count and integrate the array N times. (This may be an outdated optmiziation).
If you meant global
Below I added a different set of code, just in case you meant that var openTriggers is global, kinda like you wrote above. Also I used querySelectorAll for this which is like jQuery's $('.thing') selector.
anyhoo, I also added
var openTriggers = document.querySelectorAll('.trigger-dialog--open');
var n = openTriggers.length;
function openDialog(ddId){
for (var i = 0;i < n; i++) {
openTriggers[i].style.display = 'none';
};
document.getElementById(ddId).style.display = 'block';
};
for (var i = 0;i < n; i++) {
openTriggers[i].addEventListener("click", function (event) {
openDialog(event.target.getAttribute('data-dialog-id'));
});
}
}
So for the question of hiding already open modals I would suggest you could either cache the open Dialog within a module, or you could toggle a class, which would be less efficient since it would require an extra DOM search. Additionally you could add a if this.openModal.id === clickedThingAttr to hide if open, that way you got a toggle feature.
Anyways I suggest you read up on this stuff, if you want to use plain JS but would like the features of jQuery: http://blog.romanliutikov.com/post/63383858003/how-to-forget-about-jquery-and-start-using-native
Thank you for your time.
You can use a closure
var openTriggers = document.getElementsByClassName('trigger-dialog--open');
for (var i = 0; i < this.openTriggers.length; i++) {
(function(element) {
element.addEventListener("click", function (event) {
element.openDialog(event.target.getAttribute('data-dialog-id'));
}, false)
})(openTriggers[i]);
}
i created music blog using soundcloud widgets. I trigger a button "Play all", so when you hit it, it plays all widgets.
$(function(){
var playAll = $('.playAll');
var widget0 = SC.Widget(playAll.find('iframe')[0]);
var widget1 = SC.Widget(playAll.find('iframe')[1]);
var widget2 = SC.Widget(playAll.find('iframe')[2]);
widget0.bind(SC.Widget.Events.FINISH,function() {
widget1.play();
widget0.unbind(SC.Widget.Events.FINISH);
});
widget1.bind(SC.Widget.Events.FINISH,function() {
widget2.play();
widget1.unbind(SC.Widget.Events.FINISH);
});
$("#playSound").click(function() {
widget0.toggle();
});
});
It works, but what i'm trying to do is "for" loop, but because lack of js / jquery skills, it doesn't work.
I love the enthusiasm to refactor code and the desire to use a higher level pattern, looping, instead of hard coding like you have. That being said the "middle space" problem is one of the harder problems to solve when iterating over a list.
You have to decide on a couple of things:
You have a task your going to perform n-1 times so you have to decide if your going to skip the first element or the last.
What task your going to perform in each iteration.
What you'll do after the loop finishes.
If you'll use a functional solution or a classic "for" loop.
Lets start with the task. We'll take the task from your example and put it in a function.
function connectFrames(now, next) {
now.bind(SC.Widget.Events.FINISH, function() {
next.play();
now.unbind(SC.Widget.Events.FINISH);
});
}
With this function we can use the index from our loop to pass in the current and next elements. The only exception will be when we are on the last element when there will be no "next", so that is the one we will skip. We will use the jQuery each for a modern / functional solution.
//One query to select all iframes inside .playAll
var playAll = $('.playAll iframe');
//The jQuery "loop" http://api.jquery.com/each/
playAll.each(function(index) {
var now = playAll[index],
next = playAll[index + 1];
//If their is no next... skip.
if(!next) return;
//Use the frames we have in our function
connectFrames(now, next);
});
The last thing we have to do is write in our final step which would be to start playing the content and we are done. Here is the complete code:
function connectFrames(now, next) {
now.bind(SC.Widget.Events.FINISH, function() {
next.play();
now.unbind(SC.Widget.Events.FINISH);
});
}
//One query to select all iframes inside .playAll
var playAll = $('.playAll iframe');
//The jQuery "loop" http://api.jquery.com/each/
playAll.each(function(index) {
var now = playAll[index],
next = playAll[index + 1];
//If their is no next... skip.
if(!next) return;
//Use the frames we have in our function
connectFrames(now, next);
});
//initiator from example
$("#playSound").click(function() {
playAll[0].toggle();
});
Assuming the HTML looks somewhat like this :
<div class="playAll">
<div>
<iframe></iframe>
<iframe></iframe>
<iframe></iframe>
</div>
</div>
You get get the list of all iframes inside the div with class "playAll" (no matter the depth), and iterate:
var iframes = $(".playAll iframe");
for (i = 0; i < iframes.length; i++) {
// do something with iframes[i]
}
http://api.jquery.com/descendant-selector/
I'm new to programming and was wondering how to make a customized alert that shows the id or class name of the object when I click on it. My site has a picture of 8 different animals, and I want it so that every time I click on one of the animals there's an alert with "This is a (animal's name)". Why won't my javascript code below work?
should i be using "this" instead of "parama"? i don't understand whether or not to have any parameters for my function clicky.
var images = new Array()
images[0] = "bison"
images[1] = "frog"
function clicky(parama){
for (entry in images){
if (parama.attributes["name"].value === images[entry]){
$(parama).onClick(alert("This is a" + parama.attributes["name"].value));
} else {
$(parama).onClick(alert("dang it");
}
}
}
using sort of a combination of both your answers, I figured out a way to do it with a lot less code than I originally had. Check it out! (images all had classes of "pic")
$('.pic').click(function(){
alert("This is a " + this.getAttribute('alt'))
});
I'd recommend to use the title or alt attribute on images instead of a JS array - that's more SEO friendly and semantic. Not to mention that alt is required on images to make your HTML valid - DEMO
var images = document.getElementsByTagName("img");
for ( var i = 0, count = images.length; i < count; i++ ) {
images[i].addEventListener("click", function() {
alert( this.getAttribute("alt") );
});
}
UPDATE
if you open to use jQuery - DEMO
$("img").on("click", function() {
alert( $(this).prop("alt") );
});
You can use .click() but it's recommended to use .on() instead to attach different kind of event listeners to elements. jQuery also provides a shorthand for getting the properties - .prop()
I'm a beginner in JS and want to know a way to simplify this code. There are 7 different divs with iframes, and also 7 different links. I have shown 1 div with iframe and 1 link. I have no idea where to start. Any help would be greatly appreciated.
NOTE: The code works to my needs, but I just need to simplify it (less js code in html, and more in js file).
JavaScript in .js file:
function show_visibility(){
for(var i = 0,e = arguments.length;i < e;i++){
var myDiv = document.getElementById(arguments[i]).style;
myDiv.display = "block";}
}
function hide_visibility(){
for(var i = 0,e = arguments.length;i < e;i++){
var myDiv = document.getElementById(arguments[i]).style;
myDiv.display = "none";}
}
function refFrame() {
for(var i = 0,e = arguments.length;i < e;i++){
document.getElementById(arguments[i]).src = document.getElementById(arguments[i]).src;
}
}
Div/iframe to be modified:
<div id="r1-box">
<iframe id="frame-box1" class="work" src="youtubelink" width="720" height="405" frameborder="0"></iframe>
</div>
Link to execute JS:
<a id="r1" href="javascript:refFrame('frame-box2','frame-box3','frame-box4','frame-box5','frame-box6','frame-box7');show_visibility('r1-box');hide_visibility('r2-box','r3-box', 'r4-box','r5-box','r6-box','r7-box');">
</a>
As a beginner you shouldn't start using jQuery until you understand Javascript more.
There are a few ways you could simplify this, the most immediate one would be to get the Javascript out of the link and into a Javascript file, or at the top of the page:
window.onload = function() {
document.getElementById('#r1').onclick = function() {
refFrame('frame-box2','frame-box3','frame-box4','frame-box5','frame-box6','frame-box7');
show_visibility('r1-box');
hide_visibility('r2-box','r3-box', 'r4-box','r5-box','r6-box','r7-box');
};
// more...
};
window.onload is an event which fires once the page has - you guessed it - finished loading. There are better ways of doing this, but this is about as basic as it gets. I'd advise you look at javascript domready?
After looking at your code a bit more, I realised all your seven links will do essentially the same thing. You can simply this by using a single function:
function refClick(id) {
var i = 7,
frames = [],
boxes = [];
while(i--) {
if(i != id) {
frames.push('frame-box' + i);
boxes.push('r' + i + '-box');
}
}
refFrame.apply(null, frames);
hide_visibility.apply(null, boxes);
show_visibility('r' + id + '-box');
}
What I'm doing here is looping through 7 times, and building an array of arguments for the refFrame and hide_visibility functions. The id variable tells the loop not to put in that id into the arrays.
Using the .apply method, I can apply an array as the arguments and call it normally.
For each of your links, you can apply the following function
document.getElementById('#r1').onclick = function() {
refClick(1);
};
document.getElementById('#r2').onclick = function() {
refClick(2);
};
//.....
You could start using jQuery.
http://jquery.com/