jQuery single class selector not firing when target has multiple classes - javascript

I cannot figure out why my jQuery event handler does not fire in this cirucmstance. I have exhausted my research on Stack Overflow.
HTML:
<div class='pst-header-ad parenting pst-subscribe-motherhood-kit'>
<div class='pst-header-ad-wrap'>
<h3></h3>
<p></p>
</div>
</div>
JS, which does not fire as it should when user clicks element with class pst-subscribe-motherhood-kit:
var subscribeForms = [
{
formclass : "pst-subscribe-motherhood-kit",
formnumber : "151496",
formtitle : "Get Your FREE Mom Kit Now!",
formdescription : "Leave your email below and we'll send you our Mom Kit packed full of printables that will make your mom journey more fun. You'll also receive email updates from me from time to time. Enjoy your Mom Kit!!",
formredirect : "https://pintsizedtreasures.com/landing-pages/mom-bundle-offer/"
}, {
formclass : "pst-subscribe-marriage-kit",
formnumber : "151501",
formtitle : "Get Your FREE Marriage Kit Now!",
formdescription : "Where should we send your Marriage Kit? Leave your email below and you'll receive three printables: a praying for your husband printable, simple, romantic ideas printable and love notes printable. You'll also receive email updates from time to time from me. Enjoy your Marriage Kit!",
formredirect : "https://pintsizedtreasures.com/landing-pages/marriage-bundle-offer/"
}
];
for (var i = 0; i < subscribeForms.length; i++) {
jQuery("." + subscribeForms[i].formclass).click(function(e) {
e.preventDefault();
for (var j = 0; j < subscribeForms.length; j++) {
if (this.className == subscribeForms[j].formclass) {
var formnumber = subscribeForms[j].formnumber;
var formtitle = subscribeForms[j].formtitle;
var formdescription = subscribeForms[j].formdescription;
loadForm(formnumber, formtitle, formdescription);
}
}
});
}
FYI, this loop dynamically loads event handlers from an object of various subscription form data. The selector looks like this in practice:
jQuery(".pst-subscribe-motherhood-kit").click(function(e) { ... }
Research as shown me that when pst-subscribe-motherhood-kit is the element's class value alone, without any other class, jQuery fires as expected.
WORKS: <div class="pst-subscribe-motherhood-kit">Some text</div>
DOES NOT WORK: <div class="my-class pst-subscribe-motherhood-kit">Some text</div>
As always, any help is appreciated.
EDIT: added the object that is iterated through. And, YES, the DOM is loaded before this function is called. It is called in the footer.

You can use let instead of var for creating individual scope in each iteration of the for loop:
var subscribeForms = [{formclass: 'pst-header-ad-wrap'},{formclass: 'pst-header-ad-wrap-2'}];
for (let i = 0; i < subscribeForms.length; i++) {
jQuery("." + subscribeForms[i].formclass).click(function(e) {
e.preventDefault();
console.log(subscribeForms[i].formclass);
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='pst-header-ad parenting pst-subscribe-motherhood-kit'>
<div class='pst-header-ad-wrap'>
pst-header-ad-wrap
<h3></h3>
<p></p>
</div>
<div class='pst-header-ad-wrap-2'>
pst-header-ad-wrap-2
<h3></h3>
<p></p>
</div>
</div>

Related

create element (commenting box) in *ngFor loop

i've built a website i.e. a blog which runs at the moment locally with the full MEAN (MongoDB, Node, Angular and Express) stack. I've already implemented the backend, but have some problems for the frontend. Most of it works. I load the content dynamically i.e. every time the user reached the bottom of the page, i load the next three blog posts. I've also added some voting (up and down). I don't know if i did it right, but it works. I have an array which contains all blog post. Every time the user reached the bottom, i add them via .push to my array. This array is shown via *ngFor, so it contains all current blog post and "future posts". The question arise how i add the voting in an efficient way? I've added an array, every time a user clicks "vote up" i check if the post id is already in that array, if not i add it and increment the vote by one and store the value also in the database by a request. If the user regrets his decision he can anytime press the same button to bring the initial state back, because now the post id is in that array and therefore a click on the same button is not a vote up anymore rather we decrease the vote and remove the post id from the array (and store the new value in the database). The user can now again vote up, because the post id is no more in that array, etc.
Is there any better approach to implement the voting? I think if the user loads 1000 blog articles and votes many articles and afterwards regrets one, then the loading time can eventually very high (finding a value in a big array, etc.).
The point why i've written here is the following. The main problem is that i want to add a commenting box to each blog post. But how do i do that? Should i create each time the browser loads 3 article 3 commenting boxes? "This" is not really a problem, but what happened in the same scenario as already mentioned? What happened if the user reads 20 articles one after the other. Then 20 commenting boxes are created and are binded to some variables, etc. It cant be efficient in that way. I thought i could create one commenting box and every time the user clicks on an anchor tag (named "Reply") the commenting box will be opened. But there exists only one box. It will be shown in the appropriate place. But i don't know how to implement this with angular. :(
I'd appreciate any hint or help. :)
UPDATE 26.11.2017: i share some content from my project, so can probably better understand what i mean.
This is my blog.component.ts
import { Component, OnInit } from '#angular/core';
import { CarouselConfig } from 'ngx-bootstrap/carousel';
import { BlogPost } from '../shared/blogPost';
import { BlogPostFactory } from '../shared/blogPost-factory';
import { DataService } from '../data.service';
#Component({
selector: 'ca-blog',
templateUrl: './blog.component.html',
styleUrls: ['./blog.component.css'],
providers: [{provide: CarouselConfig, useValue: {interval: 1500, noPause: true}}]
})
export class BlogComponent implements OnInit {
private blogPosts: BlogPost[] = [];
private heartsUp: string[] = [];
private likesUp: string[] = [];
constructor(private dataService: DataService) {
window.onscroll = () => {
let windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
let body = document.body;
let html = document.documentElement;
let docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
let windowBottom = windowHeight + window.pageYOffset;
if(windowBottom >= docHeight) {
let urlSearchParams = { last: this.blogPosts.length };
this.dataService.getBlogPosts(urlSearchParams).subscribe( result => {
for(let i=0; i<result.length; i++){
this.blogPosts.push(BlogPostFactory.fromObject(result[i]));
}
});
}
};
}
ngOnInit() {
this.dataService.getBlogPosts().subscribe( result => {
for(let i=0; i<result.length; i++){
this.blogPosts.push(BlogPostFactory.fromObject(result[i]));
}
});
}
incrementHearts(index) : void {
let idIndex : number = this.heartsUp.indexOf(this.blogPosts[index].id);
if(idIndex != -1){
this.blogPosts[index].hearts--;
this.heartsUp.splice(idIndex, 1);
} else {
this.blogPosts[index].hearts++;
this.heartsUp.push(this.blogPosts[index].id);
}
this.dataService.editBlogPost(this.blogPosts[index].id, { hearts: this.blogPosts[index].hearts }).subscribe().unsubscribe();
}
incrementLikes(index) : void {
let idIndex : number = this.likesUp.indexOf(this.blogPosts[index].id);
if(idIndex != -1){
this.blogPosts[index].likes--;
this.likesUp.splice(idIndex, 1);
} else {
this.blogPosts[index].likes++;
this.likesUp.push(this.blogPosts[index].id);
}
this.dataService.editBlogPost(this.blogPosts[index].id, { likes: this.blogPosts[index].likes }).subscribe().unsubscribe();
}
}
and blog.component.html is given by
<div class="container">
<div *ngFor="let blogPost of blogPosts; let i=index">
<div *ngIf="i !== 0">
<hr class="my-5">
</div>
<div class="row">
<div class="col">
<article>
<section>
<header style="" class="mb-4">
<h1 style="display:inline" class="m-0 p-0">{{ blogPost.title }}</h1><small style="opacity:0.5" class="d-block d-sm-inline ml-sm-3">zuletzt bearbeitet am {{ blogPost.lastEdited | date }} von <strong>{{ blogPost.author.username }}</strong></small>
<p class="mt-1 mt-sm-auto"><i class="fa fa-tags mr-2"></i><a *ngFor="let hashtag of blogPost.hashtags" href="#" class="badge badge-secondary mr-2">#{{ hashtag }}</a></p>
</header>
<div class="m-0 p-0" [innerHTML]="blogPost.body">
</div>
<div>
<small class="heartUp"><a (click)="incrementHearts(i)" [ngStyle]="{'color':heartsUp.includes(blogPost.id) ? '#E63946' : 'rgba(0,0,0,0.5)'}"><i class="fa fa-heart mr-1"></i>{{ blogPost.hearts }}</a></small>
<small class="likeUp"><a (click)="incrementLikes(i)" [ngStyle]="{'color':likesUp.includes(blogPost.id) ? '#3b5998' : 'rgba(0,0,0,0.5)'}"><i class="fa fa-thumbs-up mr-1"></i>{{ blogPost.likes }}</a></small>
<small class="reply"><a><i class="fa fa-mail-reply mr-1"></i>Reply</a></small>
</div>
</section>
</article>
</div>
</div>
</div>
</div>
I hope this visualize my problem better.
for your first question it depends how you think about it, you want to disable the vote up? if yes then you need to treat it on the front end side, if not, you can the user let press the vote up and then in the server check for the user id and send a error message back ("you already voted"), for your second question i don't know very well what you mean with there is just 1 box? you mean you have 1 box component? if that makes you confusion, just think on the component as a class that you can instantiate in objects, same for components you can create a lot of since very one is a diferent instance.

Loading specific div based on the results of multiple dropdowns

EDIT: Would the approach be much easier if the Javascript listed was removed completely, and the dropdown menus restyled as <div>'s within <li>'s, and the final div was generated by a Javascript onclick event? e.g.
<a id="click_link">click me</a>
$("#click_link").click(function(){
$('#div').load('http://www.link.com/');
});
Either way, the problem at hand...
My decision to use an elegant-looking javascript solution is highlighting my massive inexperience when it comes to javascript! The problem is, on the face of it, simple...
Once an option has been chosen on each of the dropdown menus, I need a final div to load so that a specific button can be shown (a link to buy the item with the specified options, e.g. choosing Necklace D, with Stone Option B, and Delivery Option A = loading div with 'Buy' Button #17)
The dropdowns are divs that are filled and styled through the Javascript (as opposed to using the simpler <form> and <input> method), giving the flexibility to add two lines of differently styled text for each option etc. - This is where I step into the realm of the unknown and my inexperience shines through.
The isolated section is viewable in its entirity here
Ok, to the code.
Here's the Javascript:
function createByJson() {
var pearlData = [
{description:'Choose your pearl...', value:'Pearls', text:'Pearls'},
{description:'Beautiful black stone', value:'Black Pearl', text:'Black Pearl'},
{description:'Classic white stone', value:'White Pearl', text:'White Pearl'}
];
$("#DropItPearls").msDropDown({byJson:{data:pearlData, name:'pearls', width: 200}}).data("dd");
var blodeuweddData = [
{description:'Choose your item...', value:'Blodeuwedd', text:'the Blodeuwedd Collection'},
{description:'A striking statement', value:'BlodeuweddCelticStatement', text:'Celtic Statement Piece'},
{description:'Gold laced flower and pearl', value:'BlodeuweddBracelet', text:'Bracelet'},
];
$("#DropItBlodeuwedd").msDropDown({byJson:{data:blodeuweddData, name:'blodeuwedd', width: 250}})
.msDropDown({on:{change:function(data, ui) {
var val = data.value;
if(val!="")
window.location = val;
}}}).data("dd");
var deliveryData = [
{description:'Choose your method...', value:'Delivery', text:'Delivery Options'},
{description:'4-6 weeks delivery', value:'Four Weeks', text:'Made To Order'},
{description:'(unavailable on this item)', value:'Rush', text:'Express Delivery', disabled:true}
];
$("#DropItDelivery").msDropDown({byJson:{data:deliveryData, name:'delivery', width: 200, selectedIndex: 1}}).data("dd");
paymentData = [
{ description:'How would you like to pay?', value:'Payment', text:'Payment Method'},
{image:'images/msdropdown/icons/Visa-56.png', description:'Secure online payment', value:'Visa', text:'Visa'},
{image:'images/msdropdown/icons/Paypal-56.png', description:'Secure online payment', value:'Paypal', text:'Paypal'},
{image:'images/msdropdown/icons/EmailPay-56.png', description:'Order by email', value:'Email Payment', text:'Send Your Details'},
{image:'images/msdropdown/icons/Mastercard-56.png', description:'(coming soon)', value:'Mastercard', text:'Mastercard', disabled:true},
{image:'images/msdropdown/icons/Collect-56.png', description:'(coming soon)', value:'Collection', text:'Order and Collect', disabled:true},
{image:'images/msdropdown/icons/Email-56.png', description:'email Menna', value:'Other Method', text:'Alternatives'}
];
$("#DropItPayments").msDropDown({byJson:{data:paymentData, name:'payments', width: 250}}).data("dd");
}
$(document).ready(function(e) {
//no use
try {
var pages = $("#pages").msDropdown({on:{change:function(data, ui) {
var val = data.value;
if(val!="")
window.location = val;
}}}).data("dd");
var pagename = document.location.pathname.toString();
pagename = pagename.split("/");
pages.setIndexByValue(pagename[pagename.length-1]);
$("#ver").html(msBeautify.version.msDropdown);
} catch(e) {
//console.log(e);
}
$("#ver").html(msBeautify.version.msDropdown);
//convert
$("select").msDropdown();
createByJson();
$("#tech").data("dd");
});
function showValue(h) {
console.log(h.name, h.value);
}
$("#tech").change(function() {
console.log("by jquery: ", this.value);
})
//
And the html:
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Item</p></div>
<div id="DropItBlodeuwedd"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Precious Stones</p></div>
<div id="DropItPearls"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Payment</p></div>
<div id="DropItPayments"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Delivery</p></div>
<div id="DropItDelivery"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Buy Now!</p></div>
<div id="DropItBuy"></div>
</div>
Again, working version viewable here
Many thanks in advance!
What I think you want is for your Buy button to dynamically read what the dropdowns currently say and build a link for redirection based on that, rather than trying to update the Buy button every time a dropdown changes.
From your code I can't see what the form of the final URL is supposed to be. For example, to get the current value of the delivery option, you can check $('#DropItDelivery :selected').text() which will be something like "Made To Order".
Your Buy Now! could be a button with a click event that reads these values and constructs the URL with basic string concatenation, e.g.:
window.location = "buynow.html?delivery=" + $('#DropItDelivery :selected').text() +
"&payment=" + $('#DropItPayments :selected').text()
// etc.
Of course you'd have to handle these options on the server.
In case you want to redirect to the payment page of the processor, you can just branch based on the payment method and give them the URL you want based on that.
var pm = $('#DropItPayments :selected').text();
if (pm == "Visa")
{
// Visa payment URL construction
}
else if (pm == "Send Your Details")
{
// Send your details URL construction
}
// etc.

Passing selected options as an array using ajax

I have a contact list as below:
<div class="contacts_list_data_contacts" id="contactlist">
<div class="contacts_checkbox_01"><input name="contact_id[]" id="contact_id" type="checkbox" value="28383" style="margin-top:0px ; margin-left:-3px;" /></div>
<div class="contacts_firstname_01_contacts">Michael</div>
<div class="ncontacts_mobile_nmbr_01">+44515665544</div>
</div>
<div class="contacts_list_data_contacts" id="contactlist">
<div class="contacts_checkbox_01"><input name="contact_id[]" id="contact_id" type="checkbox" value="28383" style="margin-top:0px ; margin-left:-3px;" /></div>
<div class="contacts_firstname_01_contacts">Katherine</div>
<div class="ncontacts_mobile_nmbr_01">+30589746621</div>
</div>
It looks like that:
Firstname MobileNumber
===========================
Michael +44515665544
Katherine +30589746621
I am using ajax request to delete a contact
contact_id=document.getElementById('contact_id_ajax').value;
xmlHttp.open("POST","?action=ajaxcontact&todo=DeleteContact&contact_id=" +contact_id ,true);
Using the below function I am able to get ONLY one contact at once by put a checkbox. I don't know how to pass all of the contacts when I select all the contacts and pass them to the ajax request.
<Script>
var field;
function get_contact(field)
{
for (i = 0; i < field.length; i++)
if(field[i].checked)
document.getElementById("contact_id_ajax").value=field[i].value;
}
</Script>
Thank you,
First things, first. You have a lot places where you use the same IDs for elements. This is very bad practice indeed. IF you want a way to identify a collection of similar items, use classes (you already have - just get rid of the ids or get unique ones).
Now, if you add a class to your checkboxes like so -
<input name="contact_id[]" class="contact_id" type="checkbox" value="28383" style="margin-top:0px ; margin-left:-3px;" />
Then in your getContacts function, you can skip the argument and go with -
function getContacts(){
var contacts = document.getElementByClassName('contact_id'), ids = [];
for (var i = 0; i < contacts.length; i += 1){
if (contacts[i].checked)
ids.push(contacts[i].value);
}
return ids;
}
Now, when you want the contacts list -
var contacts = getContacts(); //array of contact ids
//This array can now be used in your ajax request

HTML Node Map Generator?

Alright, what I'm looking for is something that could generate a graphical tree-style map of a web pages nodes.
So essentially it could theoretically transform something like this:
<aside id="leftCol">
<section class="container">
<header class="child1">
<hgroup>
<h1 class="title">Demo Project</h1>
<h3 class="subTitle">WEBSITE 2011</h3>
</hgroup>
</header>
<div class="child2" id="thisDiv">
<div class="subChild1">
<div class="anotherChild1"></div>
<div class="anotherChild2"></div>
<div class="anotherChild3"></div>
</div>
<div class="subChild2">
<p>Some Text</p>
</div>
</div>
<footer class="child3">
Link to project here
</footer>
</section>
</aside>
(This would of course be inside the HTML and BODY tags but for the sake of an example I'm going to use a snippet from my portfolio page with some generated text)
Into something like this:
Example http://www.deviantart.com/download/287437946/node_map_by_wild_fire126-d4r4sje.png
There's absolutely no design thought put into this so don't criticize it, purely for the example purpose. I made this image in photoshop quickly just to illustrate exactly what I'm talking about. All of this could be easily generated with CSS for the most part. It does not by any means have to be this graphical but for the sake of me being bored, it is.
I'm looking for a plugin or a piece of software that can do this for me. I would prefer that it would generate this map in HTML or as an image. I guess any map type would be okay as long as it would be easy to follow.
As a last resort if I can't find quite what I'm looking for I might end up just writing it myself, if that happens, I would be happy to be looking for some people to help with the coding of the plugin.
You could retrieve all children of a certain element and return their tag, class, id and depth. Then you can get creative with css to create a visual tree. Something like this should work. Example at http://jsfiddle.net/elclanrs/UHbMa/.
jQuery plugin:
$.fn.buildTree = function() {
var tree = {};
this.find('*').andSelf().each(function(i, v) {
var parents = $(this).parents().length - 1;
for (var i = 0; i < parents; i++) {
tree[v.tagName.toLowerCase()] = {
id: v.id,
className: v.className,
depth: i
}
}
});
return tree;
};
And then you call it like:
var tree= $('aside').buildTree(),
html = '';
for (tag in tree) {
html += '<p><strong>Tag:</strong> ' + tag +
' | <strong>Class:</strong> ' + tree[tag].className +
' | <strong>Depth:</strong> ' + tree[tag].depth;
}
$('#tree').append(html);
Graphviz is a nice tool for doing such a thing.
You could use a piece of Javascript to generate a Graphviz file and generate a png with the tool.
The javascript should recursively visit all Elements and Generate unique IDs for every Element and write them out in the fairly easy to understand Graphviz format.
Here's a Bookmarklet to convert a page to the Graphviz format.
javascript:void((function() {var f = function(pid, e) { var id = "id" + Math.round(Math.random()*1000000), c = e.childNodes, r = id+'[label="'+(e.tagName ? e.tagName : e.nodeValue.replace(/[\n\r]+/g," "))+'"];\n'; for(var i = 0; i < c.length; i++) { r+=f(id, c[i]); }; if(pid) {r += pid + "->" + id + ";\n";}; return r;}; document.body.innerText = "digraph {\n" + f(false, document.getElementsByTagName("html")[0]) + "}"})())
Here's a quick workthrough to the format: http://www.orient-lodge.com/node/3408
Then generate a png file: (example works under Unix)
dot -Tpng < graph.dot > g.png
There's a Javascript Renderer for Graphviz, too. Canviz I haven't tried it yet, but looks promising.

sorting elements using jquery

I have a div, #containerDiv, which contains elements related to users like first name, last name etc. in separate divs. I need to sort the contents of the container div based on the last name, first name etc. values.
On searching google the examples I got all are appending the sorted results and not changing the entire HTML being displayed. They are also not sorting by specific fields (first name, last name).
So please help me in sorting the entire content of #containerDiv based on specific fields and also displaying it.
The Page looks Like something as mentioned Below:
<div id="containerDiv">
<div id="lName_1">dsaf</div><div id="fName_1">grad</div>
<div id="lName_2">sdaf</div><div id="fName_2">radg</div>
<div id="lName_3">asdf</div><div id="fName_3">drag</div>
<div id="lName_4">fasd</div><div id="fName_4">gard</div>
<div id="lName_5">dasf</div><div id="fName_5">grda</div>
<div id="lName_6">asfd</div><div id="fName_6">drga</div>
</div>
On getting sorted by last name div values, the resulted structure of the container div should look like:
<div id="containerDiv">
<div id="lName_3">asdf</div><div id="fName_3">drag</div>
<div id="lName_6">asfd</div><div id="fName_6">drga</div>
<div id="lName_5">dasf</div><div id="fName_5">grda</div>
<div id="lName_1">dsaf</div><div id="fName_1">grad</div>
<div id="lName_4">fasd</div><div id="fName_4">gard</div>
<div id="lName_2">sdaf</div><div id="fName_2">radg</div>
</div>
Now I think you all can help me in a better way.
this is a sample example:
html:
<div id="containerDiv">
<div>2</div>
<div>3</div>
<div>1</div>
</div>
js
$(function() {
var container, divs;
divs = $("#containerDiv>div").clone();
container = $("#containerDiv");
divs.sort(function(divX, divY) {
return divX.innerHTML > divY.innerHTML;
});
container.empty();
divs.appendTo(container);
});
you may set your divs.sort function param depend on your goal.
jsFiddle.
and a jQuery Plugin is suitable
I suggest you read the div values so you get an array of objects (persons for example) or just names and perform a sort operation on that. Than...output the result to the initial div (overwriting the default values).
I have built a jQuery sort function in which you can affect the sort field.
(it rebuilds the html by moving the row to another location).
function sortTableJquery()
{
var tbl =$("#tbl tr");
var store = [];
var sortElementIndex = parseFloat($.data(document.body, "sortElement"));
for (var i = 0, len = $(tbl).length; i < len; i++)
{
var rowDom = $(tbl).eq(i);
var rowData = $.trim($("td",$(rowDom)).eq(sortElementIndex).text());
store.push([rowData, rowDom]);
}
store.sort(function (x, y)
{
if (x[0].toLowerCase() == y[0].toLowerCase()) return 0;
if (x[0].toLowerCase() < y[0].toLowerCase()) return -1 * parseFloat($.data(document.body, "sortDir"));
else return 1 * parseFloat($.data(document.body, "sortDir"));
});
for (var i = 0, len = store.length; i < len; i++)
{
$("#tbl").append(store[i][1]);
}
store = null;
}
Every time I need to sort lists I use ListJs.
It's well documented, has good performance even for large lists and it's very lightweight (7KB, despite being library agnostic).

Categories

Resources