jQuery - Array element as selector - javascript

I created a little script to "like" posts on a certain forum that I frequent, the script works as intended, liking every post on a certain thread.
Now I want to improve it by making it like posts of certain users, rather than every user, so far, I managed to make it work for a specific user:
$(function(){
var TargetLink = $("[data-author='SpecificUser'] span:contains('Like')" );
if (TargetLink.length )
TargetLink.each ( function () {
var clickEvent = document.createEvent ("HTMLEvents");
clickEvent.initEvent ("click", true, true);
this.dispatchEvent (clickEvent);
} );
});
But I can't figure out how to make it work for several users since I'm very new to jquery and javascript, in fact I only started learning it just so I could make this script.
My idea is have an array with user names, make the value of the atribute "data-autor" a variable and then loop the liking function, but I can't figure out how to do it.
The html tree looks something like this:
<li id="post-####" class="message " data-author="User 1">
<div>
<div>
.
.
.
<span class="LikeLabel">Like</span>
.
.
.
<li id="post-####" class="message " data-author="User 2">
<div>
<div>
.
.
.
<span class="LikeLabel">Like</span>
I want to make something like this:
var userNames = ["User1", "User 2", "User 3",...,"User N"];
and then use the elements of the array as the value of the attribute [data-autor='userNames'] in my script, so only post from a specific list of users gets liked, but I have no idea how, I already read several questions but none seem to help me with my specific problem, all the tutorials I find are very basic and doesn't deal with using elements from an array as attribute values.

Try
// do stuff at `click` event
$("span").on("click", function(e) {
// log `user` , `html` of `span` element
console.log($(e.target).parent().data("author"), e.target.innerHTML);
});
// array of user names; note "User 6" does not exist at `html`
var userNames = ["User 1", "User 2", "User 3"
, "User 4", "User 5", "User 6"];
// loop through items within `userNames` array
var links = function links(arr) {
// return a
return $(arr).map(function(i, user) {
// create jQuery object
var TargetLink = $("[data-author='" + user + "'] span:contains('Like')");
// does element, collection exist ?
if (TargetLink.is("*")) {
// if element , collection exists, loop through collection
TargetLink.each(function() {
// do stuff
var clickEvent = document.createEvent("HTMLEvents");
clickEvent.initEvent("click", true, true);
this.dispatchEvent(clickEvent);
})
};
// return `TargetLink` element, collection as jQuery object,
// if user does not exist , will not be included in results
return TargetLink[0]
})
};
console.log(links(userNames));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div data-author="User 1"><span>Like</span>
</div>
<div data-author="User 2"><span>Like</span>
</div>
<div data-author="User 3"><span>Like</span>
</div>
<div data-author="User 4"><span>Like</span>
</div>
<div data-author="User 5"><span>Like</span>
</div>

This Link goes through your options.
I recommend this method.
Query.each:
jQuery.each(substr, function(index, item) {
// do something with `item` (or `this` is also `item` if you like)
});
STACKOVERFLOW

$() accepts an array of strings as a selector, then using your array of users, you can create an array of selectors.
var userNames = ["User1", "User 2", "User 3",...,"User N"];
var selectors = $.map(userNames, function(item){ // this creates a new array
// replace your selector placeholder with the username
var selector = "[data-author='%s'] span:contains('Like')".replace('%s', item);
return selector;
});
users = $(selectors); // Create your jquery object
// Here comes your code...
// Removed the if cause "each" will work even if length is 0
users.each ( function ( ) { // for each matched element
// Simplified your code
$(this).click(); // "this" is the "like"
} );

Related

HTML Form: How to submit all elements under a div as one array?

I have a form which contains multiple divs that contain the exact same thing. Here is an example:
<div id="element0">
textarea(name="front")
textarea(name="back")
input(type="radiobutton name="refresh")
</div>
<div id="element1">
textarea(name="front")
textarea(name="back")
input(type="radiobutton name="refresh")
</div>
<div id="element2">
textarea(name="front")
textarea(name="back")
unput(type="radiobutton name="refresh")
</div>
When I submit my form to my website, they all appear as arrays of the element names (e.g., front : { "Something here", "Another here" }, back : { "blah blah", "more blah" } ) however I want them to be all under a single div array. For example, element0 : { front : "Something here", back : "blah bah" }, element1: { front : "Another here", back : "more blah" } Is there an easy why to submit it by rather than write a script to do this?
Strait from the jquery docs
$( "form" ).submit(function( event ) {
console.log( $( this ).serializeArray() );
event.preventDefault();
});
This method .serializeArray() can also be called like this if you don't want to wait for the submit event
var formArray = $("#formIDHere").serializeArray();
Have a look Here for the documentation
After your update to the question, I have made you this Javascript function to get the desired result
Call formToJson(ContainerIDGoesHere) to have an object returned with the data in.
function formToJson(element){
let finalObject = [];
$(element).find('div').each(function(){
let innerDivId = $(this).attr('id');
let formData = {};
$(this).children().each(function(){
formData[$(this).attr('name')] = $(this).val()
});
finalObject.push({ "id":innerDivId, "formData":formData});
});
return finalObject;
}
Here's a JSFiddle with a working example; https://jsfiddle.net/6z1epu4w/

Creating Angular Element Once

I have an angular page that displays a list of items. It iterates over the list and displays it in a div. Here's the HTML code for that:
<div ng-repeat="item in items">
<div class="item-title-section">
<h1>{{item.text}}</h1>
</div>
<div class="item-special-section"
ng-show="item.type == 'FooB' ||
item.type == 'FooC' ||
item.type == 'FooD'">
<div>
<h2>Speical</h2>
<p>Type Section</p>
</div>
<div>
<span>{{item.text}}</span>
</div>
</div>
</div>
The item-title-section always shows. But I only want the item-special-section to show if the current item is of type B, C, or D. The problem here is if the list contains all 3 items I just want the div to be printed once but the text of each item printed. So in this case:
{
{
type: FooA,
title: FooA,
text: "Some text for this element"
},
{
type: FooB,
title: FooB,
text: "Some text for this element"
},
{
type: FooC,
title: FooC,
text: "Some text for this element"
}
}
The item-special-section div prints twice. Is there a way in angular to statically create an html element, meaning if it's created create it only once?
So one workaround I have figured out for your problem is to allocate the logic for showing only one special item to a separate function attached to the $scope. I have created a Plnkr Example to help demonstrate my explanation.
HTML
<div class="item-special-section"
ng-show="showSpecialSection(item.type)">
<p>I'm a special case!</p>
<p>{{item.text}}</p>
</div>
In the HTML we only allow that element to show if showSpecialSection returns true and we pass in the item.type.
JavaScript
// Define out specialItems
$scope.specialItems = ['FooB', 'FooC', 'FooE'];
// Create an array to stash the first item that fulfills
// the requirement in showSpecialSection
$scope.allowedItem = [];
$scope.showSpecialSection = function(item) {
// First we check if the item is in our defined list
// of special items
if ($scope.specialItems.indexOf(item) != -1) {
// Next depending on if allowedItem has anything,
// we either add it to allowedItem or check if it is
// already inside allowedItem
if ($scope.allowedItem.length == 0) {
$scope.allowedItem.push(item);
return true;
}
else if ($scope.allowedItem.indexOf(item) != -1) {
return true;
}
}
// return false if all other logic checks failed
return false;
}
Please let me know if this works out for you, and comment if you have questions. Cheers ~

too much HTML in an ajax script?

I read from this page that appending a lot of elements is bad practice and I should build up a string during each iteration of the loop and then set the HTML of the DOM element to that string. Does the same go for using too much HTML in the loop?
I have an AJAX script that parses JSON data. It requires adding data to different existing elements, like this:
$.ajax({
url: "url",
success: function (data) {
$(data.query.results.json.json).each(function (index, item) {
var title = item.title, // A,B,C or D
age = item.age,
background = item.background,
ingredient = item.Ingredient;
$('.'+ title+'_ingredient').html(''+ingredient+'')
$('.'+ title+'_age').html(''+age+'')
$('.'+ title+'_background').html(''+background+'')
});
},
error: function () {}
});
HTML:
<div class="A_ingredient"></div>
<div class="B_ingredient"></div>
<div class="C_ingredient"></div>
<div class="D_ingredient"></div>
<div class="A_age"></div>
<div class="B_age"></div>
<div class="C_age"></div>
<div class="D_age"></div>
<div class="A_background"></div>
<div class="B_background"></div>
<div class="C_background"></div>
<div class="D_background"></div>
Is it necessary to build up a string first? If so, can you show me how to do that?
It is purely about the time it takes to process calls to html() so they simply recommend you reduce the number of calls. In this case you could build them once in a loop then sets the div html once for each.
Update:
Based on your update, aside from all the extra trailing quotes you don't need to add (a string is a string is a string), your code is fine as is. You only hit each item once.
e.g.
$.ajax({
url: "url",
success: function (data) {
$(data.query.results.json.json).each(function (index, item) {
var title = item.title, // A,B,C or D
age = item.age,
background = item.background,
ingredient = item.Ingredient;
$('.'+ title+'_ingredient').html(ingredient);
$('.'+ title+'_age').html(age);
$('.'+ title+'_background').html(background);
});
},
error: function () {}
});
Note: If your item properties (Age, Background, Ingredient) are simple values (not objects or arrays), yo do not need the leading ''+s either.
Previous
Assuming you actually want to concatenate the results (you are only keeping the last ingredient at the moment), you could do something like this:
e.g.
$.ajax({
url: "url",
success: function (data) {
var ingredients = '';
$(data.query.results.json.json).each(function (index, item) {
var title = item.title;
var ingredient = item.Ingredient;
ingredients += ingredient;
});
$('.aclass').html(ingredients);
$('.bclass').html(ingredients);
$('.cclass').html(ingredients);
$('.dclass').html(ingredients);
},
error: function () {}
});
Which can be reduced to:
$('.aclass,.bclass,.cclass,.dclass').html(ingredients);
The contents of each div are identical in your example, so you only need a single string.
In this instance you would probably need some form of delimiter between ingredients, but your example is too vague.
e.g.
ingredients += ingredient + '<br/>';
In your example, you're setting the HTML on many different document elements.
If they're grouped in some way, for example all in a Div with ID #Container, you could build a string of the HTML and set the content of the whole Div at the end of it, something like this:
$.ajax({
url: "url",
success: function (data) {
var sHTML="";
$(data.query.results.json.json).each(function (index, item) {
var title = item.title,
background = item.background,
ingredient = item.Ingredient;
// not sure what your actual HTML is (div/span/td etc) but somethign like this?
sHTML+="<div>"; // an opening container for this item
sHTML+='<div class="'+title+'_ingredient">'+ingredient+'</div>')
sHTML+='<div class="'+title+'_age">'+title+'</div>')
sHTML+='<div class="'+title+'_background">'+background+'</div>')
sHTML+="</div>";
});
$("#Container").html(sHTML);
},
error: function () {}
});
Note I haven't tested this code, but you see the principal hopefully.
That is, build a string of the HTML then set one element at the end with the content.
I have done this a lot in a recent project and haven't seen any speed issues (maybe 50 'items' to set in my case).
HTML will initially look like this :
<div id="container">
</div>
Then end up like this (2 x items in this example) :
<div id="container">
<div>
<div class="<sometitle1>_ingredient">ingredient 1</div>
<div class="<sometitle1>_age">age 1</div>
<div class="<sometitle1>_background">background 1</div>
</div>
<div>
<div class="<sometitle2>_ingredient">ingredient 2</div>
<div class="<sometitle2>_age">age 2</div>
<div class="<sometitle2>_background">background 2</div>
</div>
</div>
subsequent calls will replace the element's content with new values, replacing the old items.
Building a string is, I would imagine, less processor-heavy than setting the html() on lots of elements individually. Each time you use html() I'm guessing that the browser has to go some way towards working out any knock-on effects like expanding the width of an element to accomodate it or whether events will still work as they did, etc - even if actual rendering is only run at the end of the process. This way you use html() once, although what you're setting is more complex.
Hope this helps.

Jquery - $(this) id from selector group

So
I've defined a simple list and an output
<div class="item-list">
<p id="One">First Item</p>
<p id="Two">Second Item</p>
<p id="Three">Third Item</p>
</div>
<div class="box">Add Stuff in Here!</div>
And
I've written this script
$(".item-list > p").click(function(){
var thisId = (this.id);
$(".box").append('<p>' + thisId + '</p>');
});
I want
each item, on click, to append its id as text inside .box element
Like So
<div class="box>One</div>
And this works
just like you'd expect. JS Fiddle of the simplified Working Example.
But in my more complex example:
the id of the selected element
(this.id)
comes back as ...
Ew..
undefined.
Here's a JS Fiddle of the more complex version of this:
http://jsfiddle.net/pQz3W/4/
What's causing these id's to return undefined?
To be clear, In the more complex example, I'm doing the same thing, it's just crowded by other functionality. Something in there is causing my id to come back as undefined.
I'll keep posting updates to this fiddle as I clean the code up more and more to pertain only to what's necessary to be there for this question!
You have an array
ServiceArray = [
[
["SplashPage", "Splash Page", "hey!"],
["GamingWebsite", "Gaming Website", "yo!"],
["BasicWebsite", "Basic Website", "hi!"],
["AdvancedWebsite", "Advanced Website", "ooh!"],
["FlexWebsite", "Flex Website", "ahh!"],
......etc
and you have ID's that you're trying to look up in that array like this
var thisId = this.id;
var outputList = ServiceArray[0][thisId][2];
but there are no associative arrays in javascript, and the keys you're looking for doesn't exist. You need to rebuild that array with objects that has keys and values that actually match what you're looking for.
try this: var thisId = $(this).attr("id");

knockout js remove does not work

I'm trying to get my head around observables in knockout js!
However, I'm facing a problem implementing the remove function on an observable array.
My js is as follow:
$(function () {
var data = [{ name: "name1" }, { name: "name2" }, { name: "name3"}];
var viewModel = {
namesList: ko.observableArray(data),
nameToAdd: ko.observable("name4"),
myCustomAddItem: function () {
this.namesList.push({ name: this.nameToAdd() });
},
myCustomRemove: function () {
console.log("before + " + this.nameToAdd());
this.namesList.remove(this.nameToAdd());
console.log("after + " + this.nameToAdd());
}
};
ko.applyBindings(viewModel);
});
and my html is:
Name To add/remove <input type="text" data-bind="value: nameToAdd, valueUpdate: 'afterkeydown'"/>
<ul data-bind="template: {name: 'listTemp1', foreach :namesList}">
</ul>
<p>
<button data-bind="click: myCustomAddItem">Add Item</button>
<button data-bind="click: myCustomRemove">Remove Item</button>
<script id="listTemp1" type="text/html">
<li data-bind="text:name"> </li>
</script>
</p>
my myCustomAddItem works fine, but not the myCustomRemove. I also have put a console.log before and after the this.namesList.remove(this.nameToAdd()); to see if anything's wrong there, but I cannot see any error in there. When I click the "Remove Item" button, firebug console shows the logs but the item's not removed from the list.
Any help appreciated
The parameter to remove should be a function which returns true or false on whether to remove something.
It works quite similarly to the filter function.
In your case, something like this should work:
myCustomRemove: function () {
console.log("before + " + this.nameToAdd());
var nameToAdd = this.nameToAdd();
this.namesList.remove(function(item) {
//'item' will be one of the items in the array,
//thus we compare the name property of it to the value we want to remove
return item.name == nameToAdd;
});
console.log("after + " + this.nameToAdd());
}
[this should be a comment on Jani answer, but I can't still comment on others post, sorry]
Just a small clarification: technically you can call remove() passing the element to remove, see section "remove and removeAll" on http://knockoutjs.com/documentation/observableArrays.html.
the problem with your code is that the elements in 'data' array are objects (containing a property called 'name'), and you are asking to remove from the array the string "name4" (or whatever 'nameToAdd' contains).
You can be tempted to create a new object to pass to remove, like this:
// old code
//this.namesList.remove(this.nameToAdd());
this.namesList.remove({ name: this.nameToAdd() });
but this still fails, because the way javascript object equality works (see, for example: How to determine equality for two JavaScript objects?).
So, in the end you need to use the function anyway.
In this simple example, you can also convert the 'namesList' array to a simple array of strings, and bind "$data" in the template. see http://jsfiddle.net/saurus/usKwA/.
In a more complex scenario, maybe you can't avoid using objects.
[observableArray].remove(function(item) { return item.[whatever] == [somevalue] ; } );

Categories