I am making a movie review web site as a project for school, and I want to put a click function on the image movie cover which will load the details and reviews of that movie. The code I'm using works but does not seem practical. The parameter in my loadReviews function is the movie ID for the database.
$(document).ready(function () {
$("#cover1").click(function () { loadReviews(1); });
$("#cover2").click(function () { loadReviews(2); });
$("#cover3").click(function () { loadReviews(3); });
$("#cover4").click(function () { loadReviews(4); });
$("#cover5").click(function () { loadReviews(5); });
$("#cover6").click(function () { loadReviews(6); });
$("#cover7").click(function () { loadReviews(7); });
$("#cover8").click(function () { loadReviews(8); });
$("#cover9").click(function () { loadReviews(9); });
$("#cover10").click(function () { loadReviews(10); });
$("#cover11").click(function () { loadReviews(11); });
$("#cover12").click(function () { loadReviews(12); });
});
As you can see I am writing each one manually. I tried using a for loop like this but does not work the way I thought.
for (i = 1; i < 12; i++) {
$("#cover" + i).click(function () { loadReviews(i); });
}
Using the loop it makes each image load the details of the same (#12) movie. Each image is assigned the class 'cover1', 'cover2' etc. I want some sort of 'loop' to automatically bind each click event to the correct image cover. I'm using a generic handler in Visual Studio 15. We must use ajax and jquery to update the page without a postback, this is how I am getting the movies and reviews.
If I need to show more code let me know. Thanks!
You could get away with having just one click handler and store the identifier as a data attribute. So your repeated HTML element might be something like this:
<div class="cover" data-movieid="1">
...
</div>
<div class="cover" data-movieid="2">
...
</div>
etc.
Then assign a single click handler and within that handler get the identifier from the element which was clicked. Something like this:
$(document).ready(function () {
$('.cover').click(function () {
var movieID = $(this).data('movieid');
loadReviews(movieID);
});
});
Depending on what loadReviews() does, you can probably make the whole thing simpler. Instead of giving everything ids and only using those in your selectors, from any given clicked element you can use jQuery to query the DOM and find the relative element you need without using an id.
If the HTML can't be changed
$("[id^=cover]").click(function () {
var rev = parseInt(this.id.replace(/\D+/g, ''));
loadReviews(rev);
});
Instead of using IDs for each cover I would recommend using a "cover" class and a data parameter with the id.
<div class"cover" data-movieid="1"></div>
and the js code would look like:
$(document).ready(function () {
$(".cover").click(function () { loadReviews($(this).data('movieid')); });
});
Using the loop it makes each image load the details of the same (#12) movie.
This happens because when the loop ends the value of the variable is always the last while the event will happen in future.
Two ways to solve it (Immediately-invoked function expression):
function loadReviews(i) {
console.log(i);
}
for (i = 1; i < 5; i++) {
(function(i) {
$("#cover" + i).on('click', function (e) {
loadReviews(i);
});
}(i));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="cover1">Cover 1</button>
<button id="cover2">Cover 2</button>
<button id="cover3">Cover 3</button>
<button id="cover4">Cover 4</button>
<button id="cover5">Cover 5</button>
The second way is based on the id (i.e: get the last part id: remove the cover string):
function loadReviews(i) {
console.log(i);
}
for (i = 1; i < 5; i++) {
$("#cover" + i).click(function () {
loadReviews(this.id.replace('cover', ''));
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="cover1">Cover 1</button>
<button id="cover2">Cover 2</button>
<button id="cover3">Cover 3</button>
<button id="cover4">Cover 4</button>
<button id="cover5">Cover 5</button>
You are facing the typical "callback inside look" context problem.
Let's check the code.
Having this:
$("#cover1").click(function () {
loadReviews(1);
});
Means that the code inside the function will be run only when the click event occurs, and not when the event is attached, so the runtime will go this way:
// First, attaches the event
$("#cover1").click(function () {
// This will be run when user clicks, so will be called last
loadReviews(1);
});
// The runtime will continue right BEFORE the click callback is run
So in your loop the runtime will go this way:
for (i = 1; i < 12; i++) {
// var i value starts from 0. And attaches the click event
$("#cover" + i).click(function () {
// Runs after all the loop is done (when the i value is 12)
loadReviews(i);
});
// loop continue BEFORE the content of the click callback is run
}
One fast way to fix your loop is to call a function with the variable declared inside the function, so it will be never changed by external actions.
function attachClick(id) {
// This will be run on each loop, creating a id variable
// that will be unique for this context
$("#cover" + id).click(function () { loadReviews(id); });
}
for (i = 1; i < 12; i++) {
// Call the function passing the i variable as parameter
attachClick(i);
}
And the way that you will se a lot around there is by creating anonymous functions (does exactly the same as above but without creating a named function elsewhere):
for (i = 1; i < 12; i++) {
(function(id) {
$("#cover" + id).click(function () {
loadReviews(id);
});
})(i);
}
Those are the fastest ways of modifying your code to have a working one, but when working with events that way, the best is to attach only one event to the parent, capture the element clicked and get the id from there. The less events attached, the faster is everything.
function loadReviews(i) {
console.log(i);
}
for (i = 1; i < 5; i++) {
(function(i) {
$("#cover" + i).on('click', function (e) {
loadReviews(i);
});
}(i));
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="btn btn-primary" id="cover1">Cover Primary</button>
<button class="btn btn-success" id="cover2">Cover Success</button>
<button class="btn btn-danger" id="cover3">Cover Danger</button>
<button class="btn btn-warning" id="cover4">Cover Warning</button>
<button class="btn btn-info" id="cover5">Cover Info</button>
Related
I have problem with my code.I'm building a spotify app for school using the spotify WEB Api. My problem is that I have a function that will use a for loop to output data in a table and also create buttons with individual ids like=
<button value="5BJeN4SVEKe204y2SiszOe" id="btn_0">Lorem</button>
<button value="0xmaV6EtJ4M3ebZUPRnhyb" id="btn_1">Lorem</button>
<button value="0rSLgV8p5FzfnqlEk4GzxE" id="btn_2">Lorem</button>
<button value="0esxMkxlIDKbkWL8Vuj35V" id="btn_3">Lorem</button>
and so on. Every button also has a value which represents an albums id. I then transformed these buttons to an array using .toArray so i could get the value i need for every single button. Is it possible to make a function in a way that when i press btn_0 it will get the value of btn_0 and then output it to the console?And then the function would do it for every button. I tried doing one but it just outputs the data from every value like here:
$(document).on('click', '.Abuttons', function(e) {
var array = $("button").toArray();
for (var i=0; i < array.length; i++) {
$.ajax({url: "https://api.spotify.com/v1/albums/"+ array[i].value +"/tracks", success: function(result) {
console.log(result);
}});
}
});
I know i have the class .Abuttons there but i tried to make a for loop before it so it would call every single button, but it didnt work. Hope you understand, and thx for all the help.
P.s My first time here so i couldnt get the formatting to work on my jquery code.
ajax success is callback method but when you are using for loop after one loop it won't wait for the first ajax callback to fire ! it goes for second loop and so on
one solution for this is to declare a variable i for counting above all then create a function for ajax call , in success callback you count requests if variable i is smaller than number of for loop you call that function again while reach to loop !
If I understand what you're asking, you don't need to build an array at all. Instead you can read the value of the clicked button within its own event handler. You can then include that value in the URL you call via AJAX to get the information, something like this:
$(document).on('click', 'button', function(e) {
$.ajax({
url: "https://api.spotify.com/v1/albums/" + this.value + "/tracks",
success: function(result) {
console.log(result);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button value="5BJeN4SVEKe204y2SiszOe" id="btn_0">Lorem</button>
<button value="0xmaV6EtJ4M3ebZUPRnhyb" id="btn_1">Lorem</button>
<button value="0rSLgV8p5FzfnqlEk4GzxE" id="btn_2">Lorem</button>
<button value="0esxMkxlIDKbkWL8Vuj35V" id="btn_3">Lorem</button>
Your buttons are probably wrapped in an element. Something like,
<div class="my-buttoms">
<button value="..."></button>
</div>
Thus, you can bind a click event in this div and get a click target.
$('.my-buttons').on('click', (e) => console.log(e.target.value))
$('.w').on('click', function(e) {
if (e.target.tagName === 'BUTTON') {
alert(e.target.value);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="w">
<button value="1">1</button>
<button value="2">2</button>
</div>
You can use jQuery $(this) to get the event target value of the value attribute:
$('[id^=btn_]').on('click', function(e) {
$.ajax({
url: "https://api.spotify.com/v1/albums/" + $(this).attr("value") + "/tracks",
success: function(result) {
console.log(result);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button value="5BJeN4SVEKe204y2SiszOe" id="btn_0">Lorem</button>
<button value="0xmaV6EtJ4M3ebZUPRnhyb" id="btn_1">Lorem</button>
<button value="0rSLgV8p5FzfnqlEk4GzxE" id="btn_2">Lorem</button>
<button value="0esxMkxlIDKbkWL8Vuj35V" id="btn_3">Lorem</button>
So, I feel like you may be overcomplicating it:
https://jsfiddle.net/bashaen/vewae2t7/3/
$('button'); in JQuery already returns back an array of all elements it's targeted.
var arr = $("button");
// Actually Contains - [button, button, button, button]
$.each will filter through the buttons much like the for loop, but there are different benefits.
$.each(arr, function(i, e) { // i = index, e = element
console.log( $(e).val() );
// or if you're more comfortable with it.
console.log( $(this).val() );
});
Also, do both a .success() and a .fail(), to see if your ajax is actually going through.
I have some JavaScript to execute logic i.e. doSomething() when a button is clicked. I know the class of the buttons, but there are multiple buttons on the page with this same class. The problem with my code below is that the doSomething() function is executed once for every button on the page when I only want it to execute one time only.
var myButtonClass = $(".my-button-class");
if (myButtonClass) {
myButtonClass.click(function (event) {
if (someCondition) {
doSomething();
}
});
}
I know it would be better to select by button div, but the button div names all vary based on how many there are (i.e. #my-button-div1, #my-button-div2, etc.) and the number of buttons is indefinite.
Is there a way to only do this event once? I don't care which button in the class happens to be clicked, I just need the event to fire once and then it's done.
UPDATE: To be clear, I still want the logic to execute if the user clicks another button on the page again, so I don't want to completely unbind the event. I just don't want it to execute once for every button on the page. For example, let's say I have 4 buttons. Right now it's doing something like the following when just one button is clicked:
alert!
alert!
alert!
alert!
I only need ONE of those alerts. Basically, whenever the user clicks any of the buttons on the page, I need it to go alert! only once per click.
Revised so that you don't have weird if statements, and allows for other click handlers to happily work if binded elsewhere
UPDATED:
var clickHandler = function handleClick(event) {
doSomething();
}
var bindClicks = true;
function doSomething() {
alert('alert!');
}
function doTheBinding() {
if (bindClicks) {
$('.my-button-class').on('click', clickHandler);
bindClicks = false;
}
}
// as many times as you want but it will still call once
doTheBinding();
doTheBinding();
doTheBinding();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button class="my-button-class">Click me!</button>
<button class="my-button-class">Click me!</button>
<button class="my-button-class">Click me!</button>
<button class="my-button-class">Click me!</button>
You can use on() and off() to bind and unbind the event on an element respectively.
$('.my-button-class').on('click', function (event) {
if (someCondition) {
doSomething();
// Unbind the event on all the elements having the class
$('.my-button-class').off('click');
// To unbind the event on only the clicked element
// $(this).off('click');
}
});
Sidenote: if (myButtonClass) { will always evaluate to true. jQuery returns an object even when the element is not found and an object is always truthy. To check if an element exists in DOM, use length property on the jQuery object $('someSelector').length > 0.
If you give the handler a local boolean variable that is protected with a closure, you can create a function that will execute only once. See this SO answer.
Run the code snippet below to see it in action.
$(".my-button-class").click(function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
doSomething();
}
};
}());
function doSomething() {
alert("You should only see me once!");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="my-button-class">Click me!</button>
<button class="my-button-class">No click me!</button>
UPDATE: To address a different issue of the click event getting bound more than once. Just do a similar thing with:
var bind = function() {
var bound = false;
return function() {
if (!bound) {
bound = true;
$(".my-button-class").click(function() {
if (someCondition)
doSomething();
});
}
};
}();
bind();
bind(); // won't execute second time
var someCondition = true;
function doSomething() {
$("#output").append("<br>alert!");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="my-button-class">Click me!</button>
<button class="my-button-class">No click me!</button>
<span id="output"></span>
I guess this code does not work, because at DOM load jQuery caches its objects and bind the functions to them?
$('span.button.slide_out').on('click', function () {
$(this).toggleClass('slide_out').toggleClass('slide_in');
$('#testbox').slideDown();
});
$('span.button.slide_in').on('click', function () {
$(this).toggleClass('slide_out').toggleClass('slide_in');
$('#testbox').slideUp();
});
I know I could write this easily with slideToggle or something else, but I have to fire different actions on every first and every second click. How can I achieve this using the same selector (instead of creating two different selectors)?
JS FIDDLE
The binding is indeed done on DOM creation, but that doesn't have to be a problem in this case, it also means that the button is still clicked if it no longer has the slide_out class. Therefore you can reuse the same click event and check the current state to choose whether to slide up or down. For example:
$('.slide_out').on('click', function () {
if($(this).toggleClass('slide_out slide_in').hasClass('slide_in'))
$('#testbox').slideDown();
else
$('#testbox').slideUp();
});
Fiddle
You could use the solution from Event binding on dynamically created elements?, as suggested by https://stackoverflow.com/users/502381/juhana:
HTML:
<span class="button_container"><span class="button slide_out">Click me</span></span>
<div id="testbox">Whohoohoooo, I am slidiiing!<br><br><small>Hey… wait! Why I am not sliding up again?</small></div>
JS:
$('.button_container').on('click', '.slide_out', function () {
$(this).toggleClass('slide_out').toggleClass('slide_in');
$('#testbox').slideDown();
});
$('.button_container').on('click', '.slide_in', function () {
$(this).toggleClass('slide_out').toggleClass('slide_in');
$('#testbox').slideUp();
});
http://jsfiddle.net/ag3cpcfb/
But, in my opinion it would be better to make your code simpler by using slideToggle() and adjust your css classes:
HTML:
<span class="button">Click me</span>
<div id="testbox">Whohoohoooo, I am slidiiing!<br><br><small>Hey… wait! Why I am not sliding up again?</small></div>
JS:
$('.button').on('click', function () {
var $testbox = $('#testbox');
if ($testbox.is(':visible')) {
console.log('Click 1');
} else {
console.log('Click 2');
}
$(this).toggleClass('slide_in');
$testbox.slideToggle();
});
http://jsfiddle.net/k77ferjh/
But this fires "Click 1" all of the time if you repeatedly click on the button. If this is not an issue, fine, if it is, you can also use a number to keep track of your clicks:
JS:
var clicks = 0;
$('.button').on('click', function () {
clicks++;
if (clicks % 2 == 0) {
console.log('Slide out');
} else {
console.log('Slide in');
}
$(this).toggleClass('slide_in');
$('#testbox').slideToggle();
});
http://jsfiddle.net/k77ferjh/1/
Okay So I what to have 3 buttons
<div id="button1" onclick="choose1()">Button1</div>
<div id="button2" onclick="choose2()">Button2</div>
<div id="button3" onclick="choose3()">Button3</div>
And a start button
<div id="startButton" onclick="noFunction()">Start</div>
I want to make it so that pressing on of the 3 option buttons it changes what function will be called from the start button and the background image of the start button should change.
Is there a way to do this with just javascript or do I need jquery?
It also doesn't seem possible to use onclick on div tags, jquery to do that aswell?
jsFiddle
You can use onclick on <div> tags. But you shouldn't use onclick on any tags. Don't confuse your HTML layout and display with your JavaScript functionality. Bind your click handlers directly in the JS code (note that this solution is using jQuery):
HTML:
<div id="button1">Button1</div>
<div id="button2">Button2</div>
<div id="button3">Button3</div>
<div id="startButton">Start</div>
JS:
function choose1() {
// ...
}
function choose2() {
// ...
}
function choose3() {
// ...
}
$(function() {
$("#button1").click(choose1);
$("#button2").click(choose2);
$("#button3").click(choose3);
});
You can do it in javascript (anything possible with jQuery is possible with plain javascript, since jQuery is written in javascript).
Changing the click handler for the startButton from javascript is very straightforward:
document.getElementById("startButton").onclick = newFunction;
Changing the background image is also pretty simple:
document.getElementById("startButton").style.backgroundImage = "image.png";
Obviously, you should replace newFunction and "image.png" with the function and image you actually want to use respectively.
You can say
function choose1() {
document.getElementById('startButton').onclick = function() {
alert("Button one was originally press");
}
}
jQuery IS javascript. It is just a library of functions/methods that you can call.
To solve your problem, you should write a function that changes the onclick property of your start button, and add the function you write to the onclick of the other buttons.
Like so:
function chooseOne(){
document.getElementById('startButton').onclick="/\*whatever\*/";
}
A technology like what #nbrooks said in the comments that would do this very well is AngularJS
If you give each selector button a class, you can use javascript to interate them and bind a click event. Then you can store in a data property a key which you can lookup in a json object start stores the related action handler and image. Finally in your click handler you can pull these properties and apply them to the start button by setting the onClick handler and background image of the start button.
<div class="startSelector" data-startdataid="1">Button1</div>
<div class="startSelector" data-startdataid="2">Button2</div>
<div class="startSelector" data-startdataid="3">Button3</div>
<div id="startButton">Start</div>
<script>
var startData = {
"1": {
action: function() {
alert("Button 1 was selected");
},
image: "/images/button1.jpg"
},"2": {
action: function() {
alert("Button 2 was selected");
},
image: "/images/button2.jpg"
},"3": {
action: function() {
alert("Button 3 was selected");
},
image: "/images/button3.jpg"
}
}
var changeStartButton = function(e) {
var startDataIndex = e.target.dataset.startdataid
var data = startData[startDataIndex]
document.getElementById("startButton").onclick = data.action
document.getElementById("startButton").style.backgroundImage = data.image
}
items = document.getElementsByClassName("startSelector")
for (var i = 0; i < items.length; i++) {
items[i].addEventListener("click", changeStartButton);
}
</script>
Example
http://jsfiddle.net/Xk8rv/3/
here's my code:
$(document).ready(function () {
$('.flip1').click(function () {
$('.panel1').slideToggle("slow");
});
$('.flip2').click(function () {
$('.panel2').slideToggle("slow");
});
$('.flip3').click(function () {
$('.panel3').slideToggle("slow");
});
$('.flip4').click(function () {
$('.panel4').slideToggle("slow");
});
});
I want to make a loop with .flip as the variable (flipVar) and .panel as (panelVar)
Well if it were my page I'd make sure that those elements all shared a class so that I wouldn't need to loop. However, you could do this:
for (var i = 1; i <= 4; ++i) $('.flip' + i).click((function(i) {
return function() { $('.panel' + i).slideToggle('slow'); };
})(i));
The loop variable has to be trapped in a closure so that each "click" handler references the proper value. Again, I really wouldn't do it this way. I'd make the "flip" elements share a class, and then keep that index (the implicit reference to a corresponding "panel") in a separate class element or in a "data-" attribute. Then the handler could find the panel using that value.
edit — as a hack, you could leverage the fact that the class names of the related elements are both of the form "somethingNN", where "NN" is the numeric part. You could strip off the number and then append it to "panel":
for (var i = 1; i <= 4; ++i) $('.flip' + i).click(function() {
var panelClass = this.className.replace(/.*\bflip(\d+).*/, "panel$1");
$(panelClass).slideToggle('slow');
});
Even though you want to run the selector in a loop, I wouldn't do it like that because you're doing multiple DOM selections. You can get it done with one DOM selection:
$(document).ready(function () {
$('div[class^=flip]').each(function ( idx ) {
$(this).click(function() {
$('.panel' + (idx + 1)).slideToggle("slow");
});
});
});
This will work assuming that the flip elements occur on the page in their numerical order.
This uses the attribute starts with selector to get all <div> elements that have a class name starting with "flip". Change the tag name if it is different, or remove it if they don't all have the same tag.
It uses the index that .each() makes available to you in order to toggle the correct .panel.
$(document).ready(function () {
for (var i=1; i<=4; i++) {
(function(i) {
$('.flip'+i).click(function () {
$('.panel'+i).slideToggle("slow");
});
})(i);
}
});
try this:
$(function(){
for(var i=1;i<5;i++){
$('.flip'+i).click(function () {
$('.panel'+i).slideToggle("slow");
});
}
});
PS:- don't use this it will not work as pointed out by #patrick