Plugin being triggered by every instance of a class - javascript

I am working with jQuery and have built a small plugin.
jQuery(document).ready(function(){
jQuery('.section').Section();
});
jQuery.fn.Section = function(func, options){
if(typeof(func)==='undefined') func = 'new';
if(typeof(options)==='undefined') options = new Object();
//var settings = $.extend({}, options);
var DOM = jQuery(this);
var p = DOM.parent();
var collapsed = false;
var slide_area = DOM.find('.slide_area');
var toggle_btn = DOM.find('.toggle_btn');
return this.each( function() {
switch(func){
case 'new':
toggle_btn.on('click', function(){console.log('click');
if (collapsed){
slide_area.slideDown();
toggle_btn.text('-');
collapsed = false;
}else{
slide_area.slideUp();
toggle_btn.text('+');
collapsed = true;
}
});
break;
}
});
}
You can see, I am using a class selector to attach the Section plugin to all DIV's with the class 'section'.
In the Section plugin, there is a listener for a toggle button in the section.
The problem is that when I click on the toggle button, the event is fired 4 times.(There are 4 DIV's with a 'section' class.
I thought I had this plugin set-up correctly so it plays well with jQuery. I have looked around, but could not find what I've done incorrectly.
How can I change this so it does not trigger the click function once for each instance of a 'section' DIV?
Here is the HTML to help understand the structure:
<div class="section"><!-- paypal settings -->
<h3>Paypal Settings</h3>
<div class="section_controls">
<div class="toggle_btn">-</div>
<div class="hr" style="background-color: <?php echo $settings->secondary_color; ?>;"></div>
</div>
</div>

You're doing work outside your this.each(...) that should be inside it. For instance, your lines:
var slide_area = DOM.find('.slide_area');
var toggle_btn = DOM.find('.toggle_btn');
Those are outside the this.each(...) part of your plugin, and you've set DOM to (effectively) the set of elements your plugin was called on. That means that slide_area refers to all .slide_area elements in all of the sections you were called with, and that toggle_btn refers to all .toggle_btn elements in all of the sections you were called with. Later in your this.each(...), you hook up a handler using toggle_btn.on(...), and so you hook it up to all four toggle buttons four separate times.
At first glance, everything you're doing outside your this.each(...) should be inside it.

Just put your variables in the return this.each like this:
return this.each( function() {
var DOM = jQuery(this);
var p = DOM.parent();
var collapsed = false;
var slide_area = DOM.find('.slide_area');
var toggle_btn = DOM.find('.toggle_btn');
switch(func){
case 'new':
toggle_btn.on('click', function(){
console.log('click');
if (collapsed){
slide_area.slideDown();
toggle_btn.text('-');
collapsed = false;
}else{
slide_area.slideUp();
toggle_btn.text('+');
collapsed = true;
}
});
break;
}
});
}

Related

How to work with a bunch of buttons (Show/Hide) in JavaScript

I'd like to learn how work with a group of buttons without specifying Ids for the following task:
Hide and Show Div body, Div header stay show.
I was able to make one with this code:
function hide(){
document.getElementById('here').classList.toggle('hide')
}
but Hiding/Showing each individually was prove to be more difficult.
The sample here: Show/Hide Div Body
I know there is something like:
Let btns = document.querySelectorAll('buttons);
Let tables = document.getElementsByClassName('here');
function HideShow(){
//What do I put here??????? :)
}
Note: I can't use ids, because I want to add so many divs to make it dynamic.
UPDATE:::::: UPDATE:::::: UPDATE::::::
I had to do it the old fashion way, with ids!! and IF/ELSE, not cool! :(
let tables = document.getElementsByClassName('here');
var theParent = document.querySelector('#theDude');
theParent.addEventListener('click', doSomeThing, false);
function doSomeThing(e){
if( e.target!==e.currentTarget){
var clickedItem = e.target.id;
if(clickedItem=='one'){
tables[0].classList.toggle('hide')
}else if(clickedItem=='two'){
tables[1].classList.toggle('hide')
}else if(clickedItem=='three'){
tables[2].classList.toggle('hide')
}else if(clickedItem=='four'){
tables[3].classList.toggle('hide')
}
e.stopPropagation();
}
}
Pass your node lists into your Hide and show, and then iterate over them
Let btns = document.querySelectorAll('buttons);
Let tables = document.getElementsByClassName('here');
function HideShow(elemArray){ // note: I've added an elemArray parameter here
//What do I put here??????? :)
elemArray.forEach(elemArray, function(elem) {
//with each element, toggle hide or show
//elem.classList...
}
}
HTML: <button onclick="hideThisButton(this);">Button</button>
JS: let btns = document.querySelectorAll('buttons);
let tables = document.getElementsByClassName('here');
// use this to hide all the buttons
function HideShow(){
for(let i=0;i<btns.length;i++){
// you can access the index i over here
// change the logic to hide or show specific buttons
btns[i].classList.toggle('hide');
}
}
// use this method for hiding a specific button on click
function hideThisButton(event)
{
var btn = event.target;
btn.classList.toggle('hide');
}

One addEventListener seems to be deactivated by another one

In order to learn Javascript, I created a little website on local.
I have a menu composed of "li", a empty "div", and several "section".
When a click is done on a "li", the appropriate "section" goes into the empty "div" and its class="hidden" is removed.
To make this, I have a loop on my "li" and when there is a click on one of them, a function swapContent() is called inside the li.addEventListener...
Everything works fine ! But, in one specific "section", I have buttons that also work with a click and addEventListener. And that doesn't work.
When I comment the code inside my script file and test it with the console, it works but not when it's called from the script.
I also tried including the javascript code related to those buttons inside the "li" loop right after where the addEventListener (click) with the swapContent() are called and it works !
window.addEventListener("load", function() {
const frame = document.getElementById('frame');
function swapSection(liText, secId){
let section = document.getElementById(secId);
let clone = section.cloneNode(true);
clone.classList.remove('hidden');
while (frame.firstChild) frame.firstChild.remove();
frame.appendChild(clone);
let h2Select = document.getElementsByClassName('hero');
h2Select[0].innerText = liText;
};
// -- Navigation --
var liList = document.querySelectorAll('[id^="li_"]');
for (var item of liList) {
let li = item;
let liId = li.id;
let liText = li.innerText;
// if <li> has an ID show section onclick
if (liId) {
const reg = /li/i;
let secId = liId.replace(reg, 'sct');
// Display content into frame on item menu's click
li.addEventListener('click', function(){
swapSection(liText, secId);
}, false);
}
};
// Buttons that don't work
const toChange = document.getElementById('toChange');
const btnDisable = document.getElementById('disable');
const btnEnable = document.getElementById('enable');
btnEnable.addEventListener("click", function(e) {
toChange.setAttribute("disabled", false);
toChange.innerText = 'Not Clickable'
}
);
btnDisable.addEventListener("click", function(e) {
toChange.removeAttribute("disabled");
toChange.innerText = "Clickable";
});
});
The code on https://codepen.io/Franz333/pen/vqGLZE
Thank you in advance

Using click events on dynamically added DOM elements

Question: Why does it work when element is hard-coded into HTML but does not work when added dynamically via Jquery?
I am teaching my self Jquery within my self learning of javascript, and I am just creating a simple troubleshooting assistant app for the sake of learning.
I actually have my code posted here: https://repl.it/#jllrk1/OrganicBothRay.
The way I have it set up so far is the user clicks on the header block to begin, which is set up with a onetime click function to create a UL for some products at my job in which we provide IT Service.
I then am trying to be able to click each product in that list to pull troubleshooting walkthroughs for that specific product (it will guide the user based on what they click or enter).
For testing purposes I just tried having the background of the list item in which is clicked to change to red.
I cannot get this to work, or my console.log to fire telling me that the function is not getting called.
however, if I hard code in the ul into the html, using the same code for the click events, it works just fine.
Am I doing something wrong?
Just looking to gain a better understanding!
$(function () {
//*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_
//set up variables
//*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_
//var $liItems = $('.product li');
var $chief = $(".chiefblock");
var $container = $("#container");
var $this = $(this);
var $error = '';
var initList = function () {
console.log("initList initiated");
$container.append('<div class="product"><ul><li>TASC</li><li>TABE</li><li>Las Links</li><li>TerraNova</li><li>E-Direct</li></ul></div>');
$("p").text("Start by selecting a product");
}
var navItems = function (event){
console.log("navItems initiated");
var target = $(event.target);
if (target.is("li") ) {
target.css("background-color", "red" );
}
}
var nObject = function () {
$container.append('<div id = "tasc"><h2>Tasc</h2><p></p></div></div>');
$('#newItem').prepend('<h2>Item</h2>');
}
$('.chiefblock').one('click', initList)
//$('li').on('click', navItems) this i tried and does not work
$('#newObject').on('click', nObject)
$('ul').on('click', navItems)
//$liItems.on('click', navItems)this i tried and does not work
});
for dynamically added DOM elements use
$(document).on('click', '#element', function() {
console.log($(this))
})

Colorpicker not working as expected

So right now, I can dynamically create elements (2 rows of 12 blocks) and when I click on an individual block, I can change the color of it as well.
However, I am having one problem. When I click on a block to have its color changed, the color picker will pop up beside it, no issues at all. When I add a new set of rows and try to color the same block number, it will replace the color of the block from the previous row.
For example, if I color the 12th block in the first row, then add 2 new sets of rows and click on the same block in the second set, it will act as if I'm clicking on the previous set's block. I am using https://bgrins.github.io/spectrum/ as my colorPicker
Here is a link to what I have done so far:
http://codepen.io/anon/pen/bwBRmw
var id_num = 1;
var picker = null;
$(function () {
$(document).on('click', ".repeat", function (e) {
e.preventDefault();
var $self = $(this);
var $parent = $self.parent();
if($self.hasClass("add-bottom")){
$parent.after($parent.clone(true).attr("id", "repeatable" + id_num));
id_num = id_num + 1;
//picker = null;
} else {
$parent.before($parent.clone(true).attr("id", "repeatable" + id_num));
id_num = id_num + 1;
//picker = null;
}
});
});
$(".container").on("click", "a", function(e) {
var self = this;
console.log(this.id)
console.log(this)
$(self).spectrum({
color: "#f00",
change: function(color) {
$(self).css('background-color',color.toHexString());
}
});
e.stopPropagation();
})
The problem seems to be that you are cloning elements which already have the colorpicker events bound.
EDIT: I think I've managed to work around the problem by changing your use of jQuery's clone(). If you tell it to clone without including events (omitting the first parameter to clone() which defaults to false, the DOM objects will be created without the colorpicker pointing at the old ones.
Here's an example that I think is doing what you are looking for. I've just removed the true params for clone(). No changes to HTML or CSS.

Why isn't my div swap working?

So, I need a div to slide up when another slides down.
Example:
When Home button is clicked a div, we'll call it box_Home, slides down. When Games button is clicked, box_Home should slide up and then box_Games should slide down. What's happening is that they are overlapping instead of swapping out.
http://jsfiddle.net/M8UgQ/15/
var open = $('.open'),
a = $('ul').find('a');
console.log(a.hasClass('active'));
open.click(function(e) {
e.preventDefault();
var $this = $(this),
speed = 500;
var link_id = $this.attr('id');
var box_id = '#box_' + link_id;
console.log(box_id);
if($this.hasClass('active') === true) {
$this.removeClass('active');
$(box_id).slideUp(speed);
} else if(a.hasClass('active') === false) {
$this.addClass('active');
$(box_id).slideDown(speed);
} else {
a.removeClass('active')
$(box_id).slideUp(speed);
$this.addClass('active');
$(box_id).delay(speed).slideDown(speed);
}
});
take a look at this
http://jsfiddle.net/rWrJ9/1/
the main idea is...
if the element clicked is active, remove it, otherwise: 1. find (if any) already active elements (using $('.active')) and use jQuery.map() to make them inactive and slide them up, and 2. make the element clicked active.
I also removed the unneeded variable a
IMPORTANT: the this inside the map() function is different from the this (or rather, $this as you called it) outside the map() function
I think you're saying you have two buttons id="Home" class="open" and id="Game" class="open", and two divs id="box_Home" and id="box_Game". If so, you add class="box" to box_Home and box_Game and do something like this:
$('.open').click(function(e) {
e.preventDefault();
var $this = $(this);
var link_id = $this.attr('id');
var box_id = '#box_' + link_id;
$('.box').slideUp();
$(box_id).slideDown();
});
Hi check this fiddle i hope you need thing to implement
jsfiddle
in the if else statement you are doing a mistake
else if(a.hasClass('active') === false) {
replace it with
else if($this.hasClass('active') === false) {

Categories