Combining sequential jQuery selectors into a single function - javascript

BIG EDIT: Updated the code sections, but the question remains the same.
I've got perhaps a unique jQuery issue that I could use some help on.
I've got a function (that works perfectly) that looks like this...
var openslide1 = function() {
$("#slide1").animate({
left: "+=250px",
}, 400, function() {
// Animation complete.
});
$(this).unbind('click', openslide1); \\ to prevent from pushing slide over too many times
};
$("#openslide1").click(openslide1);
My issue is there could be a potentially unlimited number of slides, and therefore "openslides". Each "openslide" selector corresponds with its own unique slide. (e.g. #openslide2 would open #slide2 and unbind #openslide2...so on and so forth).
So how do I combine these into a single variable and function?
And as a side note...I'm also going to want to "re-bind" the #openslide on the click of another event. Currently, I'm able to do them one at a time through this....
$("#closeslide1").click(function() {
$("#slide1").animate({
left: "-=250px",
}, 500, function() {
// Animation complete.
});
$('#openslide1').click(openslide1); \\ rebinds openslide1
});
But once this becomes a combined function, I'm assuming "openslide1" will no longer be the variable for the function. So how would I rebind that click event when #closeslide is clicked. (And obviously I'm going to use the first part of this question to apply to the closeslide variables as well.)
<div class="section">
<a class="edit-btn" id="openslide1" href="#null">edit</a>
<a class="edit-btn" id="openslide2" href="#null">edit</a>
<a class="edit-btn" id="openslide3" href="#null">edit</a>
<a class="edit-btn" id="openslide4" href="#null">edit</a>
</div>
<div class="slide" id="slide1">
<h1>Edit Row</h1>
<p>Prototyping data below, may not reflect link clicked.</p>
Submit <a id="closeslide1" class="btn btn-danger" style="color:#fff" href="#null">Cancel</a>
</div>
<div class="slide" id="slide2">
<h1>Edit Row</h1>
<p>Prototyping data below, may not reflect link clicked.</p>
Submit <a id="closeslide2" class="btn btn-danger" style="color:#fff" href="#null">Cancel</a>
</div>
<div class="slide" id="slide3">
<h1>Edit Row</h1>
<p>Prototyping data below, may not reflect link clicked.</p>
Submit <a id="closeslide3" class="btn btn-danger" style="color:#fff" href="#null">Cancel</a>
</div>
<div class="slide" id="slide4">
<h1>Edit Row</h1>
<p>Prototyping data below, may not reflect link clicked.</p>
Submit <a id="closeslide4" class="btn btn-danger" style="color:#fff" href="#null">Cancel</a>
</div>
PLEASE HELP!
Thanks!

I'll give it a shot, does this help you with portability?
$(".openslide1").click(function() {
openSlide(".slide1", "body", 400, ".openslide1");
});
function openSlide(target, bodyObj, speed, original) {
$(target).animate({
left: "+=250px",
}, speed, function() {
// Animation complete.
});
$(bodyObj).animate({
marginLeft: "+=250px",
}, speed, function() {
// Animation complete.
});
$(original).unbind('click');
}

Change you function to pass a reference to the element (or elements) that you want to manipulate:
var openslide = function(elem) {
var id = elem.attr("id");
$("#" + id.substring(5)).animate({
left: "+=250px",
}, 400, function() {
// Animation complete.
});
$('body').animate({
marginLeft: "+=250px",
}, 400, function() {
// Animation complete.
});
elem.unbind('click', openslide);
};
$(".edit-btn").click(function() {openslide($(this))});
Then you can use a single function for all your elements and you can bind them all at the same time if they have a common class (in this case, I called them .openslide).
Edit: looking at your original question again, I noticed that the element being animated at the start isn't the same element. But if there is an easily defined relationship between it and the element you clicked on (for example, it's a child of the original element, or the next sibling, or something) you can easily come up with the appropriate selector to select it. In the example above, I assumed a sibling relationship with the target element having a class of .targetclass, but change it as suits.
Edit 2: changed it to use the id's on the assumption that id openslide1 will correspond to the element with id slide1.

Construct the links as follows :
<div class="section">
<a class="edit-btn openslide" data-slide="#slide1" href="#null">edit</a>
<a class="edit-btn openslide" data-slide="#slide2" href="#null">edit</a>
<a class="edit-btn openslide" data-slide="#slide3" href="#null">edit</a>
<a class="edit-btn openslide" data-slide="#slide4" href="#null">edit</a>
</div>
And the slides as follows :
<div class="slide" id="slide1">
<h1>Edit Row</h1>
<p>Prototyping data below, may not reflect link clicked.</p>
Submit <a class="closeslide1 btn btn-danger" style="color:#fff" href="#null">Cancel</a>
</div>
It's hard to see exactly what effect is required but the general shape of the handler will be something like this :
$(".openslide, .closeslide").click(function() {
if($("body").filter(":animated").length) { return; } //animation in progress, abort.
var $slide = $($(this).data('slide'));
var state = $slide.data('state') || 'closed';
var sign = (state == 'closed') ? '+' : '-';
$slide.animate({
left: sign + "=250px"
}, 400, function(){
$slide.data('state', (sign == '+') ? 'open' : 'closed');
});
$('body').animate({
marginLeft: sign + "=250px"
}, 400);
return false;
});
untested
As you will see, one function (when it works) will handle both slideopen and slideclose actions.
You will have to adapt the script to achieve precisely what you want.

Related

How to handle event delegation with priorities?

I have this look-a-like code in my app :
let item;
$('.parent').on('click', function () {
item = $(this).attr('item') || 0;
});
$('.parent').on('click', '.children', this, function () {
doSomething();
});
<div class="parent" item="50">
<div class="children">
</div>
</div>
<div class="parent" item="51">
<div class="children">
</div>
</div>
<div class="parent" item="52">
<div class="children">
</div>
</div>
I have a parent element with several children in it. Clicking anywhere on the parent element will give me an item information, so I will be able to load functions according to this item variable on click on children.
My problem is : I have to delegate the onClick event on children to parent element otherwise the events will trigger in this order :
Click on any child
Click on parent, which is too late because I need item variable first
I have a functionality that replaces the parent element if activated, since it was dynamically inserted into the DOM, I have to delegate the onClick event as well, like this :
$('.grandParent').on('click', '.parent', function () {
item = $(this).attr('item') || 0;
});
Now my problem is that the events are triggering in the wrong order again :
Click on any child
Click on parent
How can I manage to set click event on parent as top priority?
Edit :
I will be more specific. I have a messages list on my page, each .message element contains the message content but also some functionnalities like edit, delete, set as favorite, like, etc.
Like this :
<div class="message" data-item="1">
<a class="edit">E</a>
<a class="delete">X</a>
<a class="favorite">Fav</a>
<a class="like">Like</a>
<div class="content">
Hello world !
</div>
</div>
<div class="message" data-item="2">
<a class="edit">E</a>
<a class="delete">X</a>
<a class="favorite">Fav</a>
<a class="like">Like</a>
<div class="content">
Hello world !
</div>
</div>
Each one of those functionnalities will trigger different functions when clicked : edit(), delete(), like(), etc.
All of them will make AJAX requests which will then send the item variable to my server in order to know what item has to be impacted by this click.
To avoid repetition in my events handlers, I am trying to get the data-item attribute's value with one event bound to click on .message element, relying on the bubbling of children elements.
Get the item where you actually want it and get rid of the handler on parent:
$('.parent').on('click', '.children', function () {
item = $(this).closest('.parent').attr('item');
doSomething();
});
EDIT:
Then get the item when you click on the grandparent:
$('.grandParent').on('click', '.children', function () {
item = $(this).closest('.parent').attr('item');
});
EDIT #2:
Based on the OPs updated requirements, do it like this:
<div class="message" data-item="1">
<a class="action edit" data-type="edit">E</a>
<a class="action delete" data-type="delete">X</a>
<a class="action favorite" data-type="favorite">Fav</a>
<a class="like">Like</a>
<div class="content">
Hello world !
</div>
</div>
<div class="message" data-item="2">
<a class="action edit" data-type="edit">E</a>
<a class="action delete" data-type="delete">X</a>
<a class="action favorite" data-type="favorite">Fav</a>
<a class="like">Like</a>
<div class="content">
Hello world !
</div>
</div>
$(document).on('click','.message .action',function(e) {
const item = $(this).closest('message').attr('item');
switch($(this).data('type')) {
case 'edit': doEdit(item); break;
case 'delete': doDelete(item); break;
case 'favorite': doFavorite(item); break;
}
});
See, one event handler?
It's a very common pattern to do what you're doing. In the absence of a more robust framework (e.g. React), then using plain jQuery this is how it should be done.

in meteor how to stop functionalities to work at a time?

In meteor, I have created a cards dynamically after submitting the form. and the dynamic card contains the show and hide buttons. When I click on show option button the hidden part is showing for all the cards instead of that particular card. the problem is the card is creating dynamically so I thought that is problem .. how to make the functionality to work separately to the each card.
Here I am attaching the code:
<div id="newActionCard">
{{#each newaction}}
<div class="workflowcard">
<div class="module-card">
<div class="res-border"></div>
<div class="card-img"></div>
<div class="res-content">
<div class=" assigned-team">{{team}}</div>
<div class=" newaction-name">{{action_title}}</div><hr>
<div class="description">{{description}}</div>
<div class=" due-on">Due on:{{d_date}}</div><hr>
<div class="subcontent">
{{> actioncardsubcontent}}
</div>
<div class="reqext">
{{> requestextensioncard}}
</div>
</div>
<div class="due">
Due on:
<div>
<span class="day-stamp">THU</span><br>
<div class="date-stamp">26</div>
<div class="month-stamp">AUG
</div>
</div>
</div>
</div>
<div class="btn-box newaction">
<button type="button" class="cancelsub" >New Action</button>
<button type="submit" class="createbtnsub" >Show Options</button>
</div>
<div class="btn-box showoption">
<button type="button" class="hideoption" style="display:none">Hide Options</button>
</div>
{{/each}}
</div>
In JS I have written the hide and show functionalities in the events..now how to stop functionality to all cards at a time.
Here is my JS:
Template.workflow.events({
"click .createbtnsub" : function() {
$( ".subcontent" ).show();
$('.createbtnsub').hide();
$('.cancelsub').hide();
$('.hideoption').show();
$('.requestextension').show();
},
"click .hideoption": function(){
$('.subcontent').hide();
},
"click .hideoption": function(){
$(".subcontent").hide();
$('.cancelsub').show();
$('.createbtnsub').show();
$('.requestextension').hide();
$('.hideoption').hide();
$('.reqext').hide();
},
"click #createActionBtn": function(){
$('#createAction').hide();
$('.editw').show();
$('.hidew').show();
},
});
Template.actioncardsubcontent.rendered = function(){
this.$(".subcontent").hide();
};
Template.requestextensioncard.rendered = function(){
this.$(".reqext").hide();
};
Template.workflow.helpers({
getWorkflow: function(){
return Workflow.find();
},
user: function(){
return Meteor.users.find({});
},
getNewaction: function(){
return Newaction.find();
},
});
Please see the following and adjust the selectors as needed:
"click .showoption": function(event){
$(event.currentTarget).next('button').show();
}
This will work for selecting sibling elements, however as a tip I would recommend rewriting your template.helper that returns the cards data from the database into something more specific.
Something like dynamic classes based on index or id so your class/id names would return as follows .showoption-12kjddfse4 . Then you can just get the attribute of the current target and apply it to your js selector.
Also as kind of an explination to why all buttons were showing, is you were using the class selector, which is meant for grabbing groups of elements/nodes. This is also another reason to created more specific classnames/ids to your use case.
So in your class name you could do something like
<button class="showoption" id="{{_id}}">button</button>
<button id="hiddenoption-{{_id}}" class="hiddenoption">button</button>
Then selecting your elements would be easier as follows:
'click .showoption'(event) => {
let id = event.currentTarget.getAttribute(id);
document.getElementById('hiddenoption-'+id).style.display = 'block';
}

change img that in class in class in id

I read about it but still I'm not able to make it work.
I have an image (empty heart) that I would like to change to a different image (full heart) in order to indicate it was added to the wish list.
here is my code:
<tbody class="mainImgs">
<div ng-repeat="party in showtop5" ng-class=party.name>
<img ng-src="{{party.image}}">
<h2 id="title">{{party.title}}</h2>
<h3 id="description">{{party.description}}</h2>
<button href="#" class="imageClick" ng-click="click(party.title, party.description, party.image)">
<img ng-src="../images/emptyHeart.png" id="heart" click="myFunction(party.name, imageClick)">
</button>
<img ng-src="{{party.img}}" id="pace">
</div>
</tbody>
and my script:
function myFunction(myclass1, myclass2){
document.getElementByClass(myclass1).getElementByClass(myclass2).getElementById("heart").src = "../images/fullHeart.png";
}
what did I do wrong?
without sending the class - and trying to find image only by id - t works only for the first object heart that is printed
I think if its just about changing image then it can be handled pretty straight forward:
In html
<button href="#" class="imageClick" ng-click="click(party.title, party.description, party.image)">
<img ng-src="{{imgPath}}" id="heart" ng-click="myFunction(party.name, imageClick)">
</button>
In Controller:
$scope.imgPath = "../images/emptyHeart.png";
$scope.myFunction= function (name,imageClick){
// use promise to monitor the server response of adding product to Wishlist
("YOUR_PROMISE_CALL").then(
function(success){
$scope.imgPath = "../images/fullHeart.png"; // Bingo !!
},
function(error){
// take appropriate action
},
)
}

Running click functions on every instance of listing instead of current

I have a listing of articles here, and I can't figure out how to execute the ng-click function calls on every new article inside the ng-repeat. Right now it works for existing articles, but when new articles are added dynamically (via AJAX), I need those to have the same functionality too.
For example: the ng-click function calls on the "+" sign to reveal social buttons seem to not work once new articles are inserted via AJAX (ie: delete articles, and let list be populated again with new elements)
Does AngularJS provide any tools to do that?
<div>
<div>
<input type="text" ng-model="search">
<span>{{filtered.length}} article(s)</span>
</div>
<div article-listing ng-repeat="article in filtered = (wikiArticles | filter:search)">
<!--Individual article begin-->
<span>
{{article.title}}
</span>
<div>
<a ng-click="articles.removeArticle($index)" title="Delete">
<span>✖</span>
</a>
<a ng-click="articles.toggleShare(article)">
<span class="plus-sign" title="Share">✖</span>
<div social-share ng-show="article.socialShare">
<div ng-click="socialShare = !socialShare" class="addthis_toolbox addthis_default_style addthis_32x32_style"
addthis:title="{{article.title}}" addthis:description="{{article.extract}}" addthis:url="{{article.url}}">
<a class="addthis_button_facebook"></a>
<a class="addthis_button_twitter"></a>
<a class="addthis_button_google_plusone_share"></a>
<a class="addthis_button_reddit"></a>
<a class="addthis_button_hackernews"></a>
</div>
</div>
</a>
</div>
<div>{{article.extract}}</div>
<!--Individual article end-->
</div>
</div>
Code for ng-click calls that don't seem to work for new article insertions
$scope.articles = (function() {
return {
shuffleArticles : function() {
$scope.wikiArticles.reverse();
},
removeArticle : function(index) {
$scope.wikiArticles.splice(index, 1);
$scope.fireAPICalls();
},
toggleShare : function(currArticle) {
var previousState = currArticle.socialShare;
angular.forEach($scope.wikiArticles, function(article) {
article.socialShare = false;
});
currArticle.socialShare = previousState ? false : true;
}
}
})();
Your ng-click calls are actually working- you can watch the ng-show toggle in the debugger.
The problem is that there is nothing to display on the new items you add.
The articles you initially add all have their icons populated with the .addthis classes, for instance here's your Facebook icon element:
<a class="addthis_button_facebook at300b" title="Facebook" href="#">
<span class=" at300bs at15nc at15t_facebook">
<span class="at_a11y">Share on facebook</span>
</span>
</a>
at300bs includes the following css which displays the image:
background: url(widget058_32x32.gif) no-repeat left!important;
However as you add new items, you aren't including the needed .addthis classes to them. Their elements look like this:
<a class="addthis_button_facebook"></a>
So ng-show has nothing to display (it shows a 0x0 div).
Add the .addthis classes to your new elements as you add them and you'll be all set.

Isotope hide on click

My site has all elements displayed by default.
Isotope has the inbuilt method 'filter' - ie show ONLY this.
I'd like to make a function where I hide/show an element based on clicking on a button on screen - ie hide ONLY this (and show the others) / unhide this (and show the others).
Here's what I'm doing code-wise.
var music = $('#music').isotope();
$('nav button').on('click', function () {
music.isotope({
filter: "div." + $(this).attr("class")
});
With filtering, the filter parameter must match items in your HTML markup, if not the filter will not return anything.
FIDDLE DEMO: http://jsfiddle.net/XWVhc/1/
I can only provide an alternate example as you have not provided any html markup so that we could debug your current code, however, I'll explain how this works.
HTML FOR FILTERS:
Here we have a few simple buttons that will filter our items with the data-filter attribute attached to each button.
<div id="filter-buttons-holder">
<div class="filter-button" data-filter=".dog">DOG</div>
<div class="filter-button" data-filter=".cat">CAT</div>
<div class="filter-button" data-filter=".foo">FOO</div>
<div class="filter-button" data-filter=".bar">BAR</div>
<div class="filter-button selected" data-filter=".dog, .foo, .cat, .bar">SHOW ALL</div>
</div>
HTML FOR ISOTOPE ITEMS:
Here's the markup for our isotope items, notice that each item has a class of isotope-item and also has a class of what 'category' it belongs too, you can add multiple classes and it will still filter as expected.
<div id="module-columns-holder" class="isotope">
<a href="/" class="dog isotope-item">
<div><h1>DOG</h1></div>
</a>
<a href="/" class="cat foo isotope-item">
<div><h1>CAT</h1></div>
</a>
<a href="/" class="dog isotope-item">
<div><h1>DOG</h1></div>
</a>
<a href="/" class="foo isotope-item">
<div><h1>FOO</h1></div>
</a>
<a href="/" class="bar isotope-item">
<div><h1>BAR</h1></div>
</a>
</div>
JAVASCRIPT FILTERING
Here we set up our isotope container, notice the last data attribute is a filter, this is effectively what you're after, however you can specify which 'category' you want to filter on initially.
//Setup isotope for filters
var isotopeContainer = $('#module-columns-holder');
isotopeContainer.isotope({
itemSelector: '.isotope-item',
layoutMode : 'fitRows',
animationOptions : {
queue : false,
duration : 750,
easing: 'linear'
},
filter: '.dog, .cat, .foo, .bar'
});
CLICK EVENT FOR FILTERS
You can attach a filter to the buttons we created earlier so that you can have live filtering
$('#filter-buttons-holder .filter-button').on('click',function(){
var filters = $(this).data('filter');
var parent = $(this).closest('#filter-buttons-holder');
parent.find('.selected').removeClass('selected');
$(this).addClass('selected');
isotopeContainer.isotope({ filter: filters });
return false;
});
Hope this helps

Categories

Resources