Adding a class while looping to an array - javascript

I have a list composed by some divs, all of them have a info link with the class .lnkInfo. When clicked it should trigger a function that adds the class show to another div (like some sort of PopUp) so it is visible and when clicked again it should hide it.
I am quite certain this must be a very basic thing and most likely I will get some scoffs...but hey! Once I have this down that's one thing less I will ever have to ask again. Anyway I am starting to leave the safety of html and css to start learning JS, PHP and the like and I came to a bit of a problem.
When testing it before it was working, that was until I added another div, it only worked with the first one, reading a bit and with some suggestion I realized it must be something related to a array, the problem is that I am not quite certain of the syntax for accomplishing what I am visualizing.
Any help would be deeply appreciated.
This is my JS code and below I will attack a Fiddle of how the html looks just in case.
var infoLab = document.getElementsByClassName('lnkInfo'),
closeInfo = document.getElementById('btnCerrar');
infoLab.addEventListener('click', function () {
for (var i = 0 ; i < infoLab.length; i++) {
var links = infoLab[i];
displayPopUp('popUpCorrecto1', 'infoLab[i]');
};
});
function displayPopUp(pIdDiv, infoLab[i]){
var display = document.getElementById(pIdDiv),
for (var i = 0 ; i < infoLab.length; i++) {
infoLab[i]
newClass ='';
newClass = display.className.replace('hide','');
display.className = newClass + ' show';
};
}
JSFiddle.
Thanks a lot in advance and sorry for any facepalms!
EDIT:
This a jQuery function (in another file) that I need to call using the link because it fetches the data that will be inside the div, thus why I wanted to just add a hide/show.
$(".lnkInfo").click(function() {
var id = $('#txtId').val();
var request = $.ajax({
url: "includes/functionsLabs.php",
type: "post",
data: {
'call': 'displayInfoLabs',
'pId':id},
dataType: 'html',
success: function(response){
$('#info').html(response);
}
});
});
EDIT 2:
To a future reader of this question,
If you managed to find this answer throughout space and time, know that this is how the solution ended being, may it help you in your quest to stop being a noob.
SOLUTION

Here is a rudimentary working example of how to make a popup appear after clicking on a specific element given your current code. Note that I added an id to your link element.
// Select the element.
var infoLink1 = document.getElementById('infoLink1');
// Add an event listener to that element.
infoLink1.addEventListener('click', function () {
displayPopUp('popUpCorrecto1');
});
// Display a the popup by removing it's default "hide"
// class and adding a "show" class.
function displayPopUp(pIdDiv) {
var display = document.getElementById(pIdDiv);
var newClass = display.className.replace('hide', '');
display.className = newClass + ' show';
}
Fiddle.
There are various ways to generalize this to work for all links/popups. You could add a data-link-number=1, data-link-number=2, etc to each link element (more on data-). Select an element containing all of your links. Bind to that element an event listener that, when clicked, detects the link element that was clicked (see event delegation / "bubbling"). You can determine which link was clicked based on the value of your data-link-number attribute. Then show the appropriate popup.
You may also want to use jQuery for this. Changing an element's class by setting it's className property makes for brittle DOM code. There is an addClass and a removeClass method available. jQuery's events also work cross-browser; element.addEventListener() will not work in IE8 which still has a significant market share.

Related

Why is my JavaScript code executing only on second click?

I have been toying with this forever now. I can't seem to find out why the lightbox is only executing after I've clicked it twice. The first click, it just pops the image up in a new tab.
I've already tried using e.preventDefault (which did nothing except keep the image from popping up in a new tab after the first click).
$(document).ready(function() {
$('a[class^="fomod-"]').on('click', function() {
var fomodClass = $(this).attr('class');
var fomodGallery = $('.' + fomodClass).simpleLightbox({
loop: false
});
});
});
What I'm ultimate trying to do is watch the DOM for any clicks on links that have the "fomod-*" class and, if clicked, get the exact class of the element that was clicked. With that, the lightbox pops up and only shows the other images with that same exact class as a gallery.
The Issue
.simpleLightbox() initializes the lightbox. That means your first click adds simpleLightbox to your page, allowing all following clicks to actually trigger it.
You need to do the initialization when the page loads. Now you could do something like...
$('a[class^="fomod-"]').each(function() { ... })
But that has a few drawbacks.
It won't find elements where fomod isn't the first class, ie class="other-class fomod-one".
If you had class="fomod-one other-class", your internal selector won't work because the concatenation would result in $(".fomod-one other-class").
You'd be continuously re-initializing simpleLightbox on the same elements repeatedly, which I'm not sure if the plugin is setup to handle or not.
Solution 1 - Data Attributes
data attributes allow us a bit more flexibility on how we select our elements. Also, fetching data attributes in JavaScript is supported in both vanilla JS (using dataset) and jQuery (using .data()).
<a data-fomod="Gallery1"></a>
<a data-fomod="Gallery1"></a>
<a data-fomod="Gallery2"></a>
$(document).ready(function() {
const galleries = $("[data-fomod]")
//Get array from elements
.get()
//Attach lightbox to each unique gallery on the page
.reduce((output, {dataset}) => {
let galleryName = dataset.fomod;
return output[galleryName]
? output
: {...output, [galleryName]: $(`[data-fomod="${galleryName}"]`).simpleLightbox({loop: false})}
}, {});
});
This approach gives us three things over the initial approach:
It doesn't restrict the use of classes.
It attaches simpleLightbox to each gallery just once.
It stores the galleries individually by name in the galleries object. For example, if you wanted to tell Gallery1 to go to the next slide, you could do galleries["Gallery1"].next().
Solution 2 - Using Classes More Appropriately
As you'd mentioned in the comments, your environment doesn't provide great support for data- attributes. Instead, we can use classes, we just have to be a bit more considerate. I'll use two classes here - one to flag this as a lightbox element ("fomod"), and another to associate the gallery ("fomod-GalleryName").
You may be wondering why that "flag" fomod class is necessary. Why not just use fomod- and use the ^= selector? As mentioned above, what if fomod- is the second class behind my-other-class? The selector won't find the element.
(There are ways around this but that's opening a can of worms.)
This approach, although just slightly more involved, achieves all the same benefits that the data attribute solution does.
<a class="fomod fomod-Gallery1"></a>
<a class="fomod fomod-Gallery1"></a>
<a class="fomod fomod-Gallery2"></a>
Without comments
$(document).ready(function() {
const galleries = $(".fomod")
.get()
.reduce((output, elem) => {
let galleryName = [...elem.classList].find(c => c.startsWith('fomod-'));
if (!galleryName) return;
galleryName = galleryName.split("-")[1];
return output[galleryName]
? output
: { ...output, [galleryName]: $(`.fomod-${galleryName}`).simpleLightbox({loop: false})}
}, {});
});
With comments
$(document).ready(function() {
const galleries = $(".fomod")
//Get array from elements
.get()
//For each fomod element...
.reduce((output, elem) => {
//Get the classes on this element, and find one that starts with "fomod-"
let galleryName = [...elem.classList].find(c => c.startsWith('fomod-'));
//Didn't find one. Skip this element
if (!galleryName) return;
//Remove the "fomod-" part so we're left with just the gallery name
galleryName = galleryName.split("-")[1];
//Have we already initialized this gallery?
return output[galleryName]
//Yup. Do nothing.
? output
//Nope. Initialize it now.
: { ...output, [galleryName]: $(`.fomod-${galleryName}`).simpleLightbox({loop: false})}
}, {});
});

How can i improve my personnal jQuery Dialog plugin

I've been coding my own dialog system for exercising and to be able to customize it as i want. Here is what i've done.
$(function(){
$.fn.window = function(attr){
var $self = this;
if(!attr)
attr = {};
$.extend({
autoOpen:false
}, attr);
/**
* Create the window by updating the current jQuery block
* And adding the required classes
*/
this.create= function(){
// Already created
if($self.hasClass('window-window'))
return;
$self.addClass('window-window');
// Creating the header and appending the title
var $windowHeader = $('<div class="window-header"></div>');
var $title = $self.attr('title');
$windowHeader.html($title);
$windowHeader.append('<div class="loading-item loading-item-footer round-loading25" ' +
'data-loading-item="window" style="display:none"></div>');
// Wrapping content in a window-content class
// So the window has the proper format
$self.children().wrapAll('<div class="window-content"></div>');
$self.prepend($windowHeader);
};
/**
* Open the window in a blackish div
* With the events to close it
*/
this.open = function(){
// Creating the background
var $backgroundDiv = $('<div></div>');
$backgroundDiv.addClass('window-background');
// Making it take the size of the page
$backgroundDiv.height($(window).height());
$backgroundDiv.width($(window).width());
$self.detach().appendTo($backgroundDiv);
// The window is hidden by default, showing it
$self.show();
$('html').prepend($backgroundDiv);
// Handling closing the window
$backgroundDiv.click(function(e){
var $target = $(e.target);
if(!$target.hasClass('window-background'))
return;
$self.hide();
$self.detach().appendTo('html');
$backgroundDiv.remove();
});
};
this.create();
if(attr.autoOpen){
this.open();
}
};
});
For now i have doubt about the fact that i'm putting the window out of his native block, in the end of the html document. I wish to put it back to his position but i have no idea yet how to do it. Any idea ?
First of all, you create a jQuery function, but you do it on document.ready $(...). You should just create it, otherwise the function will not be available for other code until document has loaded.
Then you want to insert the window in the same place as the original element, for that you have insertBefore and insertAfter in jQuery. You use prepend, but that inserts it as the first element of $self.
I would urge you to look at the method chaining of jQuery which may make your code much more readable. Instead of:
// Creating the background
var $backgroundDiv = $('<div></div>');
$backgroundDiv.addClass('window-background');
// Making it take the size of the page
$backgroundDiv.height($(window).height());
$backgroundDiv.width($(window).width());
use
// Creating the background
var $backgroundDiv = $('<div></div>')
.addClass('window-background')
// Making it take the size of the page
.css({
height:$(window).height(),
width:$(window).width()
});
for example.
You also use CSS classes to store information, like if something had been clicked or not. That may be OK, but consider that you may want change the CSS classes and suddenly the functionality of your code is strongly linked to the design. Maybe using .data() instead would be better, even if you add more code to also style your elements.
You use .wrap to take the original content and put it in the window. That may be what you wanted all along, but also take a look at https://api.jquery.com/clone/ which allows you to get the elements without removing them from their original source. Again, only if it works better for you.
As a last advice, use http://jsfiddle.net to share your working code, so other people may not only comment on it, but see it in action as well.

Refer to ID (as selector) that is already inside HTML link tags

I have a link that is generated by a core module (meaning I can't modify the code) as such:
<a id="my-unique-id-1" class="my-link-class" href="/switch off">Switch off</a>
Problem is, the ID and class are within the <a> tag and I do not have any useable elements wrapped around the link that I can use.
When clicked, it goes and do what it has to do server side (see code following), and then returns this:
<a id="my-unique-id-1" class="my-link-class it-is-off" href="/switch on">Switch on</a>
I want to replace or amend the complete first link.
First the jQuery script:
$(".my-link-class").click(function() {
var current_id = $(this).attr('id');
var link = $(this).attr('href');
$.ajax({url: link, success: function (result) {
//All works fine up to here. The changes are made in server side and returns the new link as the result.
//Following is my problem:
if(result){
$(current_id).replaceWith(result); //the selector is wrong, I know.
}
}
}
My problem is that the id (current_id) is already within a <a> tag.
How can I refer to the selector in the tag.
I tried:
$(current_id).replaceWith(result); //nothing happens
$('#' + current_id).replaceWith(result);
$('a#' + current_id).replaceWith(result);
But I get with the last two TypeError: Argument 1 of Node.appendChild does not implement interface Node.
(I know I can do other things than replaceWith such as changing text and href in link, but the problem here is to find the selector first).
You can just use $(this).replaceWith():
$(document).on('click', '.my-link-class', function() {
var html = '<a id="my-unique-id-1" class="my-link-class it-is-off" href="/switch on">Switch on</a>';
$(this).replaceWith(html);
return false;
});
.it-is-off {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a id="my-unique-id-1" class="my-link-class" href="/switch off">Switch off</a>
I think there are two things happening here.
You are trying to use an ID to replace an element, when it would be easier to just keep a reference to the DOM element you want to replace rather than finding it twice.
You are binding an event to an anchor tag that you are then trying to replace. Once you replace it, the event will go away. The way to avoid this issue is bind your event to something that won't be changing. That can be the element right above the one you are going to replace, or it can be a much higher up element like the body element.
Here's a possible solution that fixes both problems. I've written a function called simulatedAjax to give an idea of what I think you're saying the backend code is doing. It follows the same idea as the jQuery $.get using the configurationObject, callback(result) signature.
function simulatedAjax(config, done){
var onOffText = (config.url === "on" ? "off" : "on");
done('Switch '+ onOffText +'');
}
And now your client code
$(function(){
// Bind the click to the body element, but with a delegate to your link class .custom-link
$('body').on('click', '.custom-link', function(e){
// Store a reference to the A tag, name is irrelevant but self is easy to understand
var self = this;
// Keep the page from actually navigating to the href
e.preventDefault();
//Replace with real jQuery $.get or $.ajax with configuration
simulatedAjax({
url: $(this).attr('href')
}, function(resultHTML){
// Since we stored a reference to the original in the self variable, we can just replace it here. Note that if we tried to use `this` here, it wouldn't refer to the right `this`
$(self).replaceWith(resultHTML);
});
});
});
You can see this code sample working in this JSFiddle http://jsfiddle.net/x83vfmuw/
Hope this helps!

jQuery slideToggle for multiple divs

What I am trying to do is have four links that each will display and hide a certain div when clicked. I am using slideToggle and I was able to get it to work with really sloppy and repetitive code. A friend of mine gave me a script he used and I tried it out and finally was able to get something to happen. However, all it does is hide the div and wont redisplay. Also it hides all the divs instead of just the specific one. Here is a jsfiddle I made. Hopefully you guys can understand what I am trying to do and help! Thanks alot.
Here is the script I'm using.
$(document).ready(function () {
$(".click_me").on('click', function () {
var $faq = $(this).next(".hide_div");
$faq.slideToggle();
$(".hide_div").not($faq).slideUp();
});
});
http://jsfiddle.net/uo15brz1/
Here's a link to a fiddle. http://jsfiddle.net/uo15brz1/7/
I changed your markup a little, adding id attributes to your divs. The jquery, gets the name attribute from the link that's clicked, adds a # to the front, hides the visible div, then toggles the respective div. I also added e.preventDefault to stop the browser from navigating due to the hash change. As an aside, javascript don't require the $ prefix.
$(document).ready(function () {
$(".click_me").on('click', function (e) {
e.preventDefault();
var name = $(this).attr('name');
var target = $("#" + name);
if(target.is(':visible')){
return false; //ignore the click if div is visible
}
target.insertBefore('.hide_div:eq(0)'); //put this item above other .hide_div elments, makes the animation prettier imo
$('.hide_div').slideUp(); //hide all divs on link click
target.slideDown(); // show the clicked one
});
});
Welcome to Stack Overflow!
Here's a fiddle:
http://jsfiddle.net/uo15brz1/2/
Basically, you need a way to point to the relevant content <div> based on the link that's clicked. It would be tricky to do that in a robust way with your current markup, so I've edited it. The examples in the jquery documentation are pretty good. Spend some time studying them, they are a great way to start out.

Javascript make a function only work once

I'm working on a blog theme where you can like posts from the theme page. It uses the following javascript to like the post with the tumblr API, change the white heart to a red heart, and also +1 to the post note count, displayed above the like buttons. It works fine, but I have the problem that when you click the heart button, it turns red, likes the post, and +1's to the note count, but you can continue to click the button once it's already liked and it keeps adding one to the note count. Can anyone help me to make it so it's a function that only works once, ex: someone clicks the heart button, it turns red, adds one to the note count, and then is done.
$(function() {
$('.likepost').live('click', function() {
var post = $(this).closest('article');
var id = post.attr('id');
var oauth = post.attr('rel').slice(-8);
var count = parseInt($("#note_count_"+ id).text());
var like = 'http://www.tumblr.com/like/'+oauth+'?id='+id;
$('#like-it').attr('src', like);
$(this).css({"background" : "url(http://static.tumblr.com/uiqhh9x/JYdlzwvnx/like2.png)"});
$("#note_count_"+ id).text(count+1);
return false;
});
});
It's functioning on http://blog.jamescharless.com/, by the way. You have to be logged into tumblr for the script to work.
$("body").one("click", ".likepost", function() {
//your code here
});
By using the .one() function you only allow the click to be triggered one time. It's kind of what it was designed for. Ideally you'd want to use a parent of .likepost closer to it than the body, but worst case you could just use body as the parent.
You can unbind the click event.
$(function() {
$('.likepost').live('click', function() {
var post = $(this).closest('article');
var id = post.attr('id');
var oauth = post.attr('rel').slice(-8);
var count = parseInt($("#note_count_"+ id).text());
var like = 'http://www.tumblr.com/like/'+oauth+'?id='+id;
$('#like-it').attr('src', like);
$(this).css({"background" : "url(http://static.tumblr.com/uiqhh9x/JYdlzwvnx/like2.png)"});
$("#note_count_"+ id).text(count+1);
// unbind
$(this).unbind('click');
return false;
});
});

Categories