Is there a more efficent way to do this? - javascript

How can I achieve this code easier or with less lines of code?
I'm curious if it can be done easier and/or more efficently. Because I feel like there is too much repetition in this, so there must be an easy way.
And I'm not only planning to make 4 of this but like 20-30, so performance is a key aspect.
Jquery:
$( document ).ready(function() {
$( "#q1" ).click(function() {
$( "#a1" ).slideToggle( "slow", function() {});
if ($(this).hasClass('on')){
$(this).removeClass('on');
}else{
$(this).addClass('on');
}
});
$( "#q2" ).click(function() {
$( "#a2" ).slideToggle( "slow", function() {});
if ($(this).hasClass('on')){
$(this).removeClass('on');
}else{
$(this).addClass('on');
}
});
$( "#q3" ).click(function() {
$( "#a3" ).slideToggle( "slow", function() {});
if ($(this).hasClass('on')){
$(this).removeClass('on');
}else{
$(this).addClass('on');
}
});
$( "#q4" ).click(function() {
$( "#a4" ).slideToggle( "slow", function() {});
if ($(this).hasClass('on')){
$(this).removeClass('on');
}else{
$(this).addClass('on');
}
});
});
HTML:
<div id="faq_content">
<div class="faq_box">
<div class="questions" id="q1">
<span>xyz</span>
</div>
<div class="answers" id="a1">
<span>xyz</span>
</div>
</div>
<div class="faq_box">
<div class="questions" id="q2">
<span>xyz</span>
</div>
<div class="answers" id="a2">
<span>xyz</span>
</div>
</div>
</div>

The easiest way that I can think of, given your HTML structure, is the following:
$(document).ready(function() {
// selecting all the elements you need to work with,
// and binding the anonymous function of the click()
// method as the event-handler:
$("#q1, #q2").click(function() {
// here $(this) will refer to the element that fired the
// click event, from that element:
$(this)
// we navigate to the next-sibling element matching the
// supplied selector:
.next('.answers')
// we use the slideToggle() method to show/hide the element,
// using an Arrow function to compose the anonymous
// function so that we can use the same this (and therefore
// $(this)) as the outer function:
.slideToggle('slow', () => {
// here $(this) still refers to the clicked element, as
// Arrow functions don't establish their own 'this'; and
// we use the toggleClass() method to add, or remove, the
// supplied class based on whether it already exists on
// the element:
$(this).toggleClass('on');
});
// here we again call the click() method, without arguments, in
// order to fire the click event on page-load (which, in this
// context will cause the answers to be hidden on page-load):
}).click();
});
$(document).ready(function() {
$("#q1, #q2").click(function() {
$(this).next('.answers').slideToggle('slow', () => {
$(this).toggleClass('on');
});
}).click();
});
*,
::before,
::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.faq_box {
border: 1px solid #000;
margin: 0.2em auto;
width: 80vw;
}
.questions {
background-color: #ffff;
border: 1px solid transparent;
border-bottom-color: #000;
cursor: pointer;
font-size: 1.2em;
font-weight: bold;
transition: background-color 0.3s linear;
}
.questions::before {
content: attr(id) ': ';
text-transform: capitalize;
}
.answers::before {
content: attr(id) ': ';
}
.on {
background-color: #0f06;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="faq_content">
<div class="faq_box">
<div class="questions" id="q1">
<span>xyz</span>
</div>
<div class="answers" id="a1">
<span>xyz</span>
</div>
</div>
<div class="faq_box">
<div class="questions" id="q2">
<span>xyz</span>
</div>
<div class="answers" id="a2">
<span>xyz</span>
</div>
</div>
</div>
References:
click().
next().
slideToggle().

$(document).ready(function(){
var qClasses = $('.q');
qClasses.on('click', function(){
var $this = $(this);
var aIds = $this.data('id');
$(aIds).slideToggle("slow");
$this.toggleClass("on");
});
});
Since all the #q1,#q2... are doing the same thing on click you can utilize the classes for this and with <div id="#q1" class="q" data-id="#a1" ></div> you can refer to the id on click of the q classes. Also, you can define the initial state of #q1 or q classes as there are only two states with class "on" or without it so the default state can be defined directly in HTML instead of checking in the JS. like: <div id="#q1" class="q on" data-id="#a1"></div>

Because all your handlers look the same, you can create a function which returns a function:
function createHandler(selector) {
return function() {
$( selector ).slideToggle( "slow", function() {});
if ($(this).hasClass('on')){
$(this).removeClass('on');
}else{
$(this).addClass('on');
}
}
}
and use it like this:
$( "#q1" ).click(createHandler("#a1"))
To find out more about this principle search for "Higher-Order Functions" and "Closures"

Related

jQuery trigger() stopPropagation() not working - bubbling occurring?

I'm working on a site that has buttons that are generated dynamically.
I'm using jQuery to toggle classes of hidden elements $('.toggle-button').on('click') (i.e. off-canvas cart, sidebar, etc.) based on data attributes
I'm using trigger('click') for the dynamic buttons and passing data through to trigger the correct hidden element. The problem is trigger() is bubbling to other buttons that have class toggle-button or that's what I think the problem is...
I've tried event.stopPropagation() but it doesn't seem to be working.
Here's a simplified version of the code I'm working with.
jQuery(document).ready(function($){
$('.toggle-button').on('click', function(event, triggerData){
console.log('toggle button triggered');
toggleClass = '';
targetSelector = '';
targetElement = '';
if ( !triggerData ) {
toggleClass = $(this).attr('data-toggle');
targetSelector = $(this).attr('data-target');
} else {
toggleClass = triggerData.toggleClass;
targetSelector = triggerData.targetSelector;
}
targetElement = $(targetSelector);
targetElement.toggleClass(toggleClass);
});
$(document).on('click', '.view-cart', function(event){
event.preventDefault();
console.log('view cart button clicked');
$('.toggle-button').trigger('click', {
toggleClass : 'show',
targetSelector : '.cart'
});
});
});
jQuery(document).ready(function($) {
$('.toggle-button').on('click', function(event, triggerData) {
console.log('toggle button triggered');
toggleClass = '';
targetSelector = '';
targetElement = '';
if (!triggerData) {
toggleClass = $(this).attr('data-toggle');
targetSelector = $(this).attr('data-target');
} else {
toggleClass = triggerData.toggleClass;
targetSelector = triggerData.targetSelector;
}
targetElement = $(targetSelector);
targetElement.toggleClass(toggleClass);
});
$(document).on('click', '.view-cart', function(event) {
event.preventDefault();
console.log('view cart button clicked');
$('.toggle-button').trigger('click', {
toggleClass: 'show',
targetSelector: '.cart'
});
});
});
.cart,
.info {
display: block;
padding: 30px;
width: 200px;
border: 1px solid #000;
visibility: hidden;
}
.cart.show,
.info.show {
visibility: visible;
}
.product {
display: inline-block;
width: 150px;
margin: 15px;
background: #e3e3e3;
text-align: center;
}
.product a {
display: block;
padding: 15px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<button class="toggle-button" data-toggle="show" data-target=".cart">View Cart</button>
<button class="toggle-button" data-toggle="show" data-target=".info">View Info</button>
<hr>
<div class="product">
<img src="https://via.placeholder.com/150">
View Cart
</div>
<div class="product">
<img src="https://via.placeholder.com/150">
View Cart
</div>
<div class="product">
<img src="https://via.placeholder.com/150">
View Cart
</div>
<div class="product">
<img src="https://via.placeholder.com/150">
View Cart
</div>
<div class="cart">
<span>This is your cart</span>
</div>
<div class="info">
<span>This is additional info</span>
</div>
</body>
</html>
there are two button with class toggle-button ...whose click event is being triggered correctly - while the problem is, that probably only one of them should be triggered. either select the element to click by a combined class attribute or by id attribute. those events are not bubbling at all, the selector just matches two elements - and that's why it subsequently clicks onto two elements.
Your code has two .toggle-button element, so $('.toggle-button').trigger() is running two times.
I recommend to name different class names of these basically.
But there is a other way not to do so, you can use :first selector as shown below.
https://api.jquery.com/first-selector/
jQuery(document).ready(function($){
$('.toggle-button').on('click', function(event, triggerData){
console.log('toggle button triggered');
toggleClass = '';
targetSelector = '';
targetElement = '';
if ( !triggerData ) {
toggleClass = $(this).attr('data-toggle');
targetSelector = $(this).attr('data-target');
} else {
toggleClass = triggerData.toggleClass;
targetSelector = triggerData.targetSelector;
}
targetElement = $(targetSelector);
targetElement.toggleClass(toggleClass);
});
$(document).on('click', '.view-cart', function(event){
event.preventDefault();
console.log('view cart button clicked');
$('.toggle-button:first').trigger('click', {
toggleClass : 'show',
targetSelector : '.cart'
});
});
});

How to create dynamic content with relative IDs using different buttons, inputs or links?

It seems to be easy, but it might be harder than it looks like, maybe. I've been having a hard time with this one.
What I'm trying to accomplish here is to create dynamic content with IDs, when clicking on any button, input or link using jQuery.
To explain better,
If .button-2 or .input-2 or .link-2 has been clicked, then the content dynamically created should be Content-1 and not Content-2 (I've got this part working right).
Then, If .button-1 or .input-1 or .link-1 gets clicked after, another content gets dynamically created and should be Content-2 (and not Content-1) and so on... (this part is also working fine)
If there is only 3 different elements I'm targeting, then only 3 contents should be created and not more. Unless I add another different target element and so on... (I need help with this)
The target elements could (but is not necessary) be clicked as of an unorderly way and still have the content with an ordered ID. (This working fine too)
In other words, you don't have to click the target elements as of an orderly way to have the content with an ordered ID.
Jsfiddle example
This code was created as an example of the main code
$(function() {
var num = 1;
$("button").on("click", function() {
$("<div />").attr("id", num).text("Content-" + num)
.appendTo($(".inner"));
num++;
});
});
.inner {
margin-top: 50px;
padding: 50px;
border: 1px solid #CCC;
}
.inner div {
background: #d0cfcf;
padding: 10px;
margin-bottom: 10px;
font-size: 22px;
color: #333;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="container">
<button type="button" class="button-1">btn 1</button>
<button type="button" class="button-2">btn 2</button>
<button type="button" class="button-3">btn 3</button>
</div>
<div class="container-2">
<input type="text" class="input-1" value="click me">
<input type="text" class="input-2" value="click on me too">
</div>
<div class="container-3">
Click here
Click here
</div>
<div class="inner"></div>
This question has been updated
Counting the buttons to answer the original question:
$(function() {
var num = 1;
$("button").on("click", function() {
if ($(".inner").children().length>=$(".container").children().length) return
$("<div />").attr("id", num).text("Content-" + num)
.appendTo($(".inner"));
num++;
});
});
.inner {
margin-top: 50px;
padding: 50px;
border: 1px solid #CCC;
}
.inner div {
background: #d0cfcf;
padding: 10px;
margin-bottom: 10px;
font-size: 22px;
color: #333;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="container">
<button type="button">btn 1</button>
<button type="button">btn 2</button>
<button type="button">btn 3</button>
</div>
<div class="inner"></div>
Here's a hardcoded, simple example.
var num = 1;
$( "button" ).on( "click", function() {
if( num === 1) {
num++;
$( "<div />" ).attr( "id", num ).text( "Content-" + num ).appendTo( $( ".inner" ) );
return;
}
if( num === 2) {
num--;
$( "<div />" ).attr( "id", num ).text( "Content-" + num ).appendTo( $( ".inner" ) );
return;
}
if( num === 3) {
num--;
$( "<div />" ).attr( "id", num ).text( "Content-" + num ).appendTo( $( ".inner" ) );
return;
}
});

How to show a indicator when clicked on a Button in this case

On Click Of the Try Again Button , is it possible to show some processing happening on the device
My jsfiddle
My code as below
$(document).on("click", ".getStarted", function(event) {
// Simulating Net Connection here
var a = 10;
if (a == 10) {
$('#mainlabel').delay(100).fadeIn(300);
$('#nonetconnmain').popup({
history : false
}).popup('open');
event.stopImmediatePropagation();
event.preventDefault();
return false;
}
});
$(document).on('click', '.nonetconnmainclose', function(event) {
$('#nonetconnmain').popup('close');
$(".getStarted").trigger("click");
event.stopImmediatePropagation();
event.preventDefault();
return false;
});
$(document).on("popupbeforeposition", "#nonetconnmain", function(event, ui) {
$('#mainlabel').hide();
});
With my code , the functionality is working , but it seems that the application is not doing any action
So my question is it possible to show any indication (For example , delay , progressbar , anything )
Here ya go
$(document).on("click", ".getStarted", function(event) {
$.mobile.loading("show", {
text: "",
textVisible: true,
theme: "z",
html: ""
});
// Simulating Net Connection here
var a = 10;
if (a == 10) {
setTimeout(function() {
$.mobile.loading("hide");
$('#mainlabel').fadeIn(300);
}, 1000);
$('#nonetconnmain').popup({
history: false
}).popup('open');
event.stopImmediatePropagation();
event.preventDefault();
return false;
}
});
$(document).on('click', '.nonetconnmainclose', function(event) {
$('#nonetconnmain').popup('close');
$(".getStarted").trigger("click");
event.stopImmediatePropagation();
event.preventDefault();
return false;
});
$(document).on("popupbeforeposition", "#nonetconnmain", function(event, ui) {
$('#mainlabel').hide();
});
.popup {
height: 200px;
width: 150px;
}
.popup h6 {
font-size: 1.5em !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>
<link href="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css" rel="stylesheet" />
<div data-role="page">
<div data-role="popup" id="nonetconnmain" data-dismissible="false" class="ui-content" data-theme="a">
<div class="popup_inner popup_sm">
<div class="popup_content" style="text-align:center;">
<p class="">Please check net connectivcty</p>
<label id="mainlabel" style="margin:100px auto 60px auto;color:Red; line-height:40px;font-size:medium;display:none">Please check</label>
</div>
<div class="popup_footer nonetconnmainclose">
<a class="">Try Again</a>
</div>
</div>
</div>
<button class="getStarted btn btn-a get_btn">Click Here</button>
</div>
You can use a small function (with time as parameter) and use jQuery animate() to create the process effect like below.
var updateProgress = function(t) {
$( "#p" ).css("width",0);
$( "#p" ).show();
$( "#p" ).animate({ "width": "100%" }, t , "linear", function() {
$(this).hide();
});
}
Do notice that the time that is chosen when calling updateProgress() is relevant with the delay and the fade in effect of the text message
updateProgress(3500);
$('#mainlabel').delay(3400).fadeIn(600);
Check it on the snippet below
var updateProgress = function(t) {
$( "#p" ).css("width",0);
$( "#p" ).show();
$( "#p" ).animate({ "width": "100%" }, t , "linear", function() {
$(this).hide();
});
}
$(document).on("click", ".getStarted", function(event) {
var a = 10;
if(a==10)
{
updateProgress(3500);
$('#mainlabel').delay(3400).fadeIn(600);
$('#nonetconnmain').popup({history: false}).popup('open');
event.stopImmediatePropagation();
event.preventDefault();
return false;
}
});
$(document).on('click', '.nonetconnmainclose', function(event) {
$('#nonetconnmain').popup('close');
$(".getStarted").trigger("click");
event.stopImmediatePropagation();
event.preventDefault();
return false;
});
$(document).on("popupbeforeposition", "#nonetconnmain",function( event, ui ) {
$('#mainlabel').hide();
});
.popup {
height: 200px;
width: 400px;
}
.popup h6 {
font-size: 1.5em !important;
}
#p {
border:none;
height:1em;
background: #0063a6;
width:0%;
float:left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>
<link href="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css" rel="stylesheet"/>
<div data-role="page">
<div data-role="popup" id="nonetconnmain" data-dismissible="false" class="ui-content" data-theme="a">
<div class="popup_inner popup_sm">
<div class="popup_content" style="text-align:center;">
<p class="">Please check net connectivcty</p>
<div id="p"></div><br>
<label id="mainlabel" style="margin:100px auto 60px auto;color:Red; line-height:40px;font-size:medium;display:none">Please check </label>
</div>
<div class="popup_footer nonetconnmainclose">
<a class="">Try Again</a>
</div>
</div>
</div>
<button class="getStarted btn btn-a get_btn">Click Here</button>
</div>
Fiddle
Probably when you click on try again , you can have a setinterval triggered which can check for online connectivity and when found can close the popup and get started again, also when we do retries in the interval the progress can be shown as progressing dots..
Below is the code, i haven't tried to run the code, but it shows the idea
$(document).on('click', '.nonetconnmainclose', function(event) {
var msgUI = $("#mainlabel");
msgUI.data("previoustext",msgUI.html()).html("retrying...");
var progress = [];
var counter = 0 ,timeout = 5;
var clearIt = setInterval(function(){
var online = navigator.onLine;
progress.push(".");
if(counter > timeout && !online){
msgUI.html(msgUI.data("previoustext"));
counter=0;
}
if(online){
$('#nonetconnmain').popup('close');
$(".getStarted").trigger("click");
counter=0;
clearInterval(clearIt);
}
else{
msgUI.html("retrying" + progress.join(""));
counter++;
}
},1000);
event.stopImmediatePropagation();
event.preventDefault();
return false;
});
Sure,
try appending a loader GIF to one of the div and remember to remove the same when your process is finished.
Kindly refer to StackOverflow
And try appending this
$('#nonetconnmain').append('<center><img style="height: 50px; position:relative; top:100px;" src="cdnjs.cloudflare.com/ajax/libs/semantic-ui/0.16.1/images/…; alt="loading..."></center>');
This will append a loader to your HTML to show some kind of processing.

Hover in/out makes my div flicker

I am trying to make a flowing-down div. I have the following jQuery code for them:
$(".centered-wrapper>main>.event").hoverIntent({
over: function() {
var pos = $(this).position();
$presentationEvent = $(this);
$fullEvent = $(this).clone();
$fullEvent.addClass("full");
$(this).css("visibility", "hidden");
$fullEvent.css({
position: "absolute",
top: pos.top,
left: pos.left
});
$(".centered-wrapper>main").append($fullEvent);
$fullEvent.find("main").slideDown(50, function() {
$fullEvent.find("footer").slideDown(50);
});
$fullEvent.animate({boxShadow: "0px 5px 5px 0px rgba(0,0,0,0.5);"}, 100);
console.log( $(".centered-wrapper>main>.event.full"));
$(".centered-wrapper>main>.event.full").on("mouseout", function() {
$(this).find("main, footer").slideUp(100);
$(this).remove();
$presentationEvent.css("visibility", "visible");
});
},
out: function() {}
});
Everything works well until I move my cursor up and down on that element, because, then it flickers and appears and disappears...
<div class="event">
<header>
<img class="photo" src="/res/users/events-photos/bal.jpg" alt=""/>
<div class="event-card">
<div class="date">
<!-- content -->
</div>
</header>
<main>
<!-- content -->
</main>
<footer>
<!-- content -->
</footer>
</div>
How can I solve this problem and where am I wrong?
You need to disable multiple queued animations. The best way to achieve this is to stop() it.
http://api.jquery.com/stop/
According to documentation:
$( "#hoverme-stop-2" ).hover(function() {
$( this ).find( "img" ).stop( true, true ).fadeOut();
}, function() {
$( this ).find( "img" ).stop( true, true ).fadeIn();
});
In your case:
$fullEvent.find("main").stop(true,true).slideDown(50, function() {
$fullEvent.find("footer").stop(true,true).slideDown(50);
});
and:
$(this).find("main, footer").stop(true,true).slideUp(100);
The visibility hidden code interferes with element hover and causes the flicker. Use another way to hide it. Like
.event:hover
{
opacity: 0;
}
To avoid events from firing use 'pointer-events: none;'
.event:hover
{
opacity: 0;
pointer-events: none;
}

$('#div').bind('scroll' function({})) not working

I have added 2 codes here the window.scroll works on my example but not the second one binding the div to the scroll.
Any one knows what am I doing wrong!?
Just so you know I'm working in MeteorJS <- I dont think that this is the problem bc. the window scrolling works.
This 2 codes are in the same js file.
$(window).scroll(function() {
lastSession = Session.get('c_info')[Session.get('c_info').current]
if(lastSession.list == 0 && $(window).height() + $(window).scrollTop() >= $(document).height()){
lastItem = $( ".list-item div:last" ).html();
if (lastSession.page == 1){
currentSession().more();
lastItem2 = $( ".list-item div:last" ).html();
} else if( lastItem2 != lastItem) {
currentSession().more();
lastItem2 = $( ".list-item div:last" ).html()
}
}
});
$('#playlist').bind('scroll',function() {
console.log("div is scrolling");
});
I tried this too:
$('#playlist').scroll(function() {
console.log("div is scrolling");
});
MeteorJS Template:
<template name="playList">
<div id="playlist" class="playlist show-for-large-up">
{{#each list}}
<a href="/video/{{_id}}" class="large-12 columns" id="pl{{v_id}}">
<div>
<div class="large-7 columns plRight">
<span>{{vTitle}}</span>
</div>
</div>
</a>
{{/each}}
</div>
</template>
Also Tried:
$('#playlist').on('scroll',function() {console.log('test')});// not working
Tried to Change the id name and putting on the document ready:
$( document ).ready(function (){
$('#pl_list').bind('scroll',function() {
console.log("div is scrolling");
});
})//failed
The div has a scrollbar and the list is long and i have a css like this:
.playlist {
padding: 0;
overflow-y: scroll;
height: 458px;
}
Also tried:
Template.playList.rendered = function () {
console.log("playlist rendered");// i can see this on logs this tells that template is in doom
Meteor.setTimeout(function(){
$('#playlist').on('scroll',function(){
console.log('Scrolling...');
});
}, 2000);// with settimeout i have giveng it 2 more seconds
}
Try this out -
$(document).ready(function(){
$('#playlist').on('scroll',function(){
console.log('Scrolling...');
});
});
Use
$('#playlist').scroll(function() {
console.log("div is scrolling");
});
instead (like you did for window).
Thats the purpose of scroll(). See jquery documentation.
Scrolling event is fired on the element, if it has scrolled. So if you only scrolling the "body" element of the DOM it will not be triggered for #playlist.
So you have put a scrollbar to the container element of #playlist. Shot answer, cut the height and add a scrollbar, then the event will fire on it.
I did a Jsfiddle http://jsfiddle.net/34j0qnpg/4/
html
<div id="playlist-wrapper">
<div id="playlist" class="playlist show-for-large-up">
<a href="/video/1" class="large-12 columns" id="pl1">
<div>
<div class="large-7 columns plRight">
<span>Titel</span>
</div>
</div>
</a>
css part
body, html {
padding: 0;
margin: 0;
background-color: lightgrey;
color: #fff;
font-family: Arial;
height: 5000px;
overflow-y:scroll;
}
#stats {
position: relative;
}
#playlist-wrapper {
border: 1px solid #000;
padding: 10px;
height: 300px;
overflow-y: scroll;
}
#playlist {
height: 1000px;
background-color: darkgrey;
}
var $stats = $('#stats');
$('#playlist-wrapper').on('scroll', function() {
$stats.html('playlist scrolling');
console.log('playlist scrolling');
});
$(window).on('scroll', function() {
$stats.html('window scrolling');
console.log('window scrolling');
});
Solved with this code:
Tried it earlyer no results, after meteorjs project reset it just automagicly workded:
Template.playList.rendered = function () {
console.log("playlist rendered");
$('#playlist').on('scroll',function(){
console.log('Scrolling...');
});
}
I answered my question just if anybody is searching for the same answer.
Thanks to anybody who tried to help me.
I LOVE THIS COMMUNITY.

Categories

Resources