About jquery storing local variable when writing plugin - javascript

Hi I am a newbie of using jQuery for writing plugin and currently facing a case on local variable handling,
and here is some code for all reference
<div class="apple"></div>
<div class="apple"></div>
<script>
var apple = [1,3];
$(function(){
$.fn.Hello = function(){
return this.each(function(){
var apple = 0;
$.fn.extend({
setApples:function(num_of_apples){
apple = num_of_apples
},
getApples:function(){
return apple;
}
});
});
}
var i = 0;
var $apples = $('.apple').Hello();
$apples.each(function(){
console.log(apple[i]);
$(this).setApples(apple[i]);
i++;
});
var j = 0;
$('.apple').each(function(){
console.log("Apple "+j+":"+$(this).getApples());
j++;
});
});
</script>
I would like the result be
Apple 0:1
Apple 1:3
But the last setting will always overwrite the last result.
Actual Results:
Apple 0:3
Apple 1:3
I try to change the variable from apple to this.apple, but it will return me undefined result. May I know what is the wrong part in my code?
I try to go to search from google and there is another alternative by using $(this).data('apple',value)for set and $(this).data('apple') for getting the value but since I would like to have some calculation on the number of apple e.g. apple++, it will be tedious to do get and set by this method
e.g $(this).data('apple',$(this).data('apple')+1);, so would like to ask whether there is any other alternative for this case, thank you.

Even if you did this.apple, you would still get the last item from the apple array. This is because you will be then using the prototype to store the value of apple which is shared across all jQuery objects. E.g
$.fn.extend({
apples:0,
setApples:function(num_of_apples){
this.apples = num_of_apples
},
getApples:function(){
return this.apples;
}
});
If you want to store a value for each element the quick way will be to actually store it on the element but use your extension methods to grab it.
$.fn.Hello = function(){
return this.each(function(){
$.fn.extend({
setApples:function(num_of_apples){
this.data("apple", num_of_apples)
},
getApples:function(){
return parseInt(this.data("apple"));
}
});
});
}
You could add checks to make sure its a number being passed into the setApples function but for now this should work.
also as a side note you can improve your loops
var i = 0;
$.each(function(){
i++
});
You can just use
$.each(function(index){
// use index here.
}
here is the documentation for it : https://api.jquery.com/each/
Also a working example of the apples problem here : https://jsfiddle.net/y27tn5wg/

Related

Using parent() in a for loop

I am creating a chrome extension that blocks all porn results on all torrent search engine sites.
So I am trying to retrieve the name of the torrents and check them against the array of strings containing blocked (adult/porn) words that I created. If it matches the array word then it should set the display of the parent element to none. But parent() from jQuery doesn't seem to work around this in a for loop. This is the code that I am using.
// 'blockedWords' is the array.
// '$("dl dt")' contains the words that I am checking against strings from
// the array 'blockedWords'.
for (var i = 0; i < $("dl dt").length; i++) {
for (var j = 0; j < blockedWords.length; j++) {
if($("dl dt")[i].innerText.indexOf(blockedWords[j]) > -1){
$(this).parent().style.display= "none"; // 1st Method or
$("dl dt")[i].parent().style.display= "none"; // 2nd Method
}
}
}
// 1st Method shows the error 'Cannot set property 'display' of undefined'
// 2nd Method shows the error '$(...)[i].parent is not a function'
// '$("dl dt")[i].parent().style.display' doesn't work but
// '$("dl dt").parent().style.display' doesn't work either
// '$("dl dt")[i].style.display' works perfectly without parent().
I have also tried 'parents()'.
Any help will be appreciated :).
As a newbie, I am also open to any other suggestions or recommendations.
And I would be really grateful if you could explain your code as well :)
And by the way, can you believe there are more than 500 porn companies out there :o :P :D
Since you have jQuery, you can avoid using nested for-loops using jQuery's filter() and JavaScript reduce(s,v):
// Filter function removes elements that return a false/falsey value like 0
$("dl dt").filter(function() {
// Save current element's innerText so we can use it within the reduce function
var str = $(this).text();
// Return sum of reduce function
return blockedWords.reduce(function(s, v) {
// For each item in blockedWords array, check whether it exists in the string. Add to total number of matches.
return s + !!~str.indexOf(v);
}, 0); // 0 = intial value of reduce function (number of matches)
}).parent().hide(); // Hide elements which pass through the filter function
Demo:
var blockedWords = [
'shit', 'fuck', 'sex'
];
$("dl dt").filter(function() {
var str = $(this).text();
return blockedWords.reduce(function(s, v) {
return s + !!~str.indexOf(v);
}, 0);
}).parent().hide();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<dl><dt>this is shit</dt></dl>
<dl><dt>this is okay</dt></dl>
<dl><dt>fuck this</dt></dl>
<dl><dt>no problem</dt></dl>
<dl><dt>sex videos</dt></dl>
EDIT: I apologize for the earlier answer if you saw it, as it was incomplete. I have also added a snippet for demonstration purposes. For further explanation of the reduce algorithm, check this answer out (basically it converts the value of indexOf to either a 0 or 1, because indexOf returns -1 if not found, or another 0-indexed integer of the position if found).
JQuery's parent function returns a JQuery object with the parent element inside of it. If you want to access the element from this object you need to retrieve the element from the object using the bracket notation.
If you were to provide some HTML I would be able to test this and make sure it works, but here is some code that could get you pointed in the right direction to use mostly JQuery instead of relying on for loops with JavaScript.
JQuery Rewrite
$("dl dt").each(function(index, element){
if($.inArray(blockedWords,$(element).text()) > -1) {
$(this).parent().css("display", "block");
$(element).parent().css("display", "block");
}
})
The Answer To Your Specific Question
Change this:
$(this).parent().style.display= "none"; // 1st Method or
$("dl dt")[i].parent().style.display= "none"; // 2nd Method
to this:
$(this).parent()[0].style.display= "none"; // 1st Method or
$($("dl dt")[i]).parent()[0].style.display= "none"; // 2nd Method
optionally, you can instead use JQuery's css function like this:
$(this).parent().css("display", "none"); // 1st Method or
$($("dl dt")[i]).parent().css("display","none"); // 2nd Method

I am trying to stop my function from displaying the same object twice when clicking a button

I have for quite some time now been trying to figure out how I can stop my code to print the same quote twice.
Also, when every single object in the array has been printed out, I'd like for it to reset somehow. So that you can browse through the quotes once you've gone through all of them.
This is the essential parts of my code:
document.getElementById('loadQuote').addEventListener("click", printQuote, false);
The printQuote function simply contains information that's accessing information from my array:
var randomObjectNumber = getRandomQuote();
var html = "<p class='quote'>"
+ quotes[randomObjectNumber].quote +
"</p>";
document.getElementById('quote-box').innerHTML = html;
One random object is displayed each time you click the eventListener:
function getRandomQuote () {
var randomObjectNumber = Math.floor(Math.random() * quotes.length );
return randomObjectNumber;
}
I have some ideas on how to do this and I have tried them but without success. I tried giving each object a boolean property but I can't really seem to assign each property a boolean value without messing the printQuote function up.
I also tried assigning the object displayed to a different array but the same problem occurred there.
I feel like there is some concepts around the eventListener that I don't fully understand, because every time I try to manipulate a displayed object I just end up changing every single object.
This is what a typical object in the array looks like by the way:
{quote : "Darkness is merely the absence of light"}
(I also have other properties assigned to the object but i feel like presenting them would be redundant)
If someone could explain, or give me a hint, on how to solve this problem I've been struggling with for some time.
Some hints would be greatly appreciated!
Have a nice day.
Sebastian.
EDIT: All code: https://jsfiddle.net/fusqb7hz/
Basically what you need:
Create a separate array that will store all quotes that you've already used.
Remove quote from initial array.
Check if you still have quotes in initial array, if not, get them back from backup array.
The problem is that you call addEventListener twice:
//Let's developers create multiple eventListeners without being redundant.
function onClicking (printFunction) {
document.getElementById('loadQuote').addEventListener("click", printFunction, false);
}
onClicking(printColor);
onClicking(printQuote);
by calling onClicking twice you make the click happen twice, so addEventListener is added twice, meaning one click counts as two.
Change the above code for this:
//Let's developers create multiple eventListeners without being redundant.
document.getElementById('loadQuote').addEventListener("click", function(){
printColor();
printQuote();
});
Here is the jsfiddle:
https://jsfiddle.net/fusqb7hz/3/
I think the easiest approach is to shuffle your quote array and then go through them one by one. This gives you the next "random" as yet unseen quote. The only part I'm not keen on is this shuffler (a derivation of Fisher Yates) modifies the original quote array. You might not care about that though.
// --------------------------------
// A bunch of quotes
// --------------------------------
var quotes = [];
quotes.push({quote : "Darkness is merely the absence of light"});
quotes.push({quote : "quote 2"});
quotes.push({quote : "quote 3"});
quotes.push({quote : "quote 4"});
quotes.push({quote : "quote 5"});
// --------------------------------
// --------------------------------
// Your favorite array shuffle utility
// --------------------------------
var shuffle = function(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
// --------------------------------
// --------------------------------
// construct a function to get a random unseen quote until
// all quotes have been seen. Then reset...
// --------------------------------
var getQuote = (function(quotes, shuffle){
var current = 0;
var get = function(){
if ( !quotes || !quotes.length ) { return ""; }
if ( current >= quotes.length ){ current = 0; }
if ( current === 0 ){
console.log("randomizing quotes...");
shuffle(quotes);
}
return quotes[current++].quote;
};
return get;
})(quotes, shuffle);
// --------------------------------
var printQuote = function(){
document.getElementById('quote').innerText = getQuote();
};
document.getElementById('loadQuote').addEventListener("click", printQuote, false);
<div id="quote"></div>
<button id="loadQuote">get quote</button>

Only allow 2 checkboxes checked

I wanna say i rarely work with javascript. It's never been my thing and it probably never will be, so i'm sorry for my lack of knowledge to this language.. Which is why my question is so humiliating.
I found this code online:
function checkboxlimit(checkgroup, limit){
var checkgroup=checkgroup
var limit=limit
for (var i=0; i<checkgroup.length; i++){
checkgroup[i].onclick=function(){
var checkedcount=0
for (var i=0; i<checkgroup.length; i++)
checkedcount+=(checkgroup[i].checked)? 1 : 0
if (checkedcount>limit){
alert("You can check a maximum of "+limit+" boxes.")
this.checked=false
}
}
}
}
<script type="text/javascript">
checkboxlimit(document.forms.checkform.weapon[], 2)
</script>
And it looks pretty basic to me. Nothing speciel here.
But, my problem is that i'm trying to find the checkbox through the name weapon[], which i'm required to as i'm outputting multiple checkboxes as weapon[] for array's.
Now my question is, how i'll make this work with this stupid little error.
To sum up:
My problem is that my checkboxes are named weapon[] (name="weapon[]") instead of (name="weapon"), as an array, and therefor the js wont recognize it.
Are you looking for something like this?
$(':checkbox[name=weapon]').on('click',function(){
var checkedBoxlength=$(':checkbox[name=weapon]:checked').length;
if(checkedBoxlength>2){
alert('You can check a maximum of 2 boxes.');
return false;
}
});
http://jsfiddle.net/umqL3/2/
--- Edit
Just made a jquery plugin just for limiting how many chekboxes you check.
(function( $ ){
$.fn.LimitCheckableNumber = function( options ) {
var settings = $.extend( {
'nr' : 1,
}, options);
return this.each(function() {
var $this=$(this),
thisName=$this.attr('name');
$this.on('click',function(){
var count=$(":checked[name='"+thisName+"']").length;
if(count>settings.nr){
alert('You can check a maximum of '+settings.nr+' boxes. ');
return false;
};
});
});
};
})( jQuery );
Link
You can't use the [] when using dot notation. Switch to treating it like an associative array.
checkboxlimit(document.forms.checkform['weapon[]'], 2)
http://jsfiddle.net/4mYbD/3/
Works just fine.
Also, you don't need to redefine checkgroup and limit. You can just use them as the arguments.

javascript - coldfusion - working with a list

This is probably easy for someone.
I am returning a list of campaignIDs (12,45,66) via JSON to a javascript variable
var campaignList = res.DATA.CAMPAIGNS
Now, given a specified campaignID passed in the URL
var campaignId ='<cfoutput>#url.campaignID#</cfoutput>'
I want to check if the returned list contains this campaignID
Any help much appreciated.
Plenty of ways to do it, but I like nice data structures, so ...
Split the list on comma, then loop over list, looking for value:
function campaignExists(campaignList,campaignId) {
aCampaignList = campaignList.split(',');
for (i=0;i<aCampaignList.length;i++) {
if (aCampaignList[i]==campaignId)
return true;
}
return false;
}
Since Array.indexOf sadly isn't cross browser, you're looking at something like:
// assume there is no match
var match_found = false;
// iterate over the campaign list looking for a match,
// set "match_found" to true if we find one
for (var i = 0; i < campaignList.length; i += 1) {
if (parseInt(campaignList[i]) === parseInt(campaignId)) {
match_found = true;
break;
}
}
If you need to do this repeatedly, wrap it in a function
Here's a bit of a "out of the box" solution. You could create a struct for your property id's that you pass into the json searilizer have the key and the value the same. Then you can test the struct for hasOwnProperty. For example:
var campaignIDs = {12 : 12, 45 : 45, 66 : 66};
campaignIDs.hasOwnProperty("12"); //true
campaignIDs.hasOwnProperty("32"); //false
This way if the list is pretty long you wont have to loop through all of the potential properties to find a match. Here's a fiddle to see it in action:
http://jsfiddle.net/bittersweetryan/NeLfk/
I don't like Billy's answer to this, variables within the function have been declared in the global scope and it is somewhat over complicated. If you have a list of ids as a string in your js just search for the id you have from user input.
var patt = new RegExp("(^|,)" + campaignId + "(,|$)");
var foundCampaign = campaignList.search(patt) != -1;

Javascript Shorthand for getElementById

Is there any shorthand for the JavaScript document.getElementById? Or is there any way I can define one? It gets repetitive retyping that over and over.
var $ = function( id ) { return document.getElementById( id ); };
$( 'someID' )
Here I used $, but you can use any valid variable name.
var byId = function( id ) { return document.getElementById( id ); };
byId( 'someID' )
To save an extra character you could pollute the String prototype like this:
pollutePrototype(String, '绎', {
configurable: false, // others must fail
get: function() {
return document.getElementById(this);
},
set: function(element) {
element.id = this;
}
});
function pollutePrototype(buildIn, name, descr) {
var oldDescr = Object.getOwnPropertyDescriptor(buildIn.prototype, name);
if (oldDescr && !oldDescr.configurable) {
console.error('Unable to replace ' + buildIn.name + '.prototype.' + name + '!');
} else {
if (oldDescr) {
console.warn('Replacing ' + buildIn.name + '.prototype.' + name + ' might cause unexpected behaviour.');
}
Object.defineProperty(buildIn.prototype, name, descr);
}
}
It works in some browsers and you can access elements this way:
document.body.appendChild(
'footer'.绎 = document.createElement('div')
);
'footer'.绎.textContent = 'btw nice browser :)';
I have chosen the name of the property almost randomly. If you actually wanted to use this shorthand I would suggest coming up with something easier to type.
You can easily create shorthand easily yourself:
function getE(id){
return document.getElementById(id);
}
id's are saved to the window.
HTML
<div id='logo'>logo</div>
JS
logo.innerHTML;
is the same as writing:
document.getElementById( 'logo' ).innerHtml;
I don't suggest using the former method as it is not common practice.
A quick alternative to contribute:
HTMLDocument.prototype.e = document.getElementById
Then just do:
document.e('id');
There's a catch, it doesn't work in browsers that don't let you extend prototypes (e.g. IE6).
(Shorthand for not only getting element by ID, but also getting element by class :P)
I use something like
function _(s){
if(s.charAt(0)=='#')return [document.getElementById(s.slice(1))];
else if(s.charAt(0)=='.'){
var b=[],a=document.getElementsByTagName("*");
for(i=0;i<a.length;i++)if(a[i].className.split(' ').indexOf(s.slice(1))>=0)b.push(a[i]);
return b;
}
}
Usage : _(".test") returns all elements with class name test, and _("#blah") returns an element with id blah.
<script>
var _ = function(eId)
{
return getElementById(eId);
}
</script>
<script>
var myDiv = _('id');
</script>
There are several good answers here and several are dancing around jQuery-like syntax, but not one mentions actually using jQuery. If you're not against trying it, check out jQuery. It let's you select elements super easy like this..
By ID:
$('#elementId')
By CSS class:
$('.className')
By element type:
$('a') // all anchors on page
$('inputs') // all inputs on page
$('p a') // all anchors within paragaphs on page
There's none built-in.
If you don't mind polluting the global namespace, why not:
function $e(id) {
return document.getElementById(id);
}
EDIT - I changed the function name to be something unusual, but short and not otherwise clashing with jQuery or anything else that uses a bare $ sign.
I frequently use:
var byId='getElementById'
var byClass='getElementsByClass'
var byTag='getElementsByTag'
var mydiv=document[byId]('div')
/* as document["getElementById"] === document.getElementById */
I think it's better than a external function (e.g. $() or byId()) because you can do things like this:
var link=document[byId]('list')[byClass]('li')[0][byTag]('a')[0]
Btw, don't use jQuery for this, jQuery is much, much slower than document.getElementById(), an external function like $() or byId(), or my method: http://jsperf.com/document-getelementbyid-vs-jquery/5
Yes, it gets repetitive to use the same function over and over each time with a different argument:
var myImage = document.getElementById("myImage");
var myDiv = document.getElementById("myDiv");
So a nice thing would be a function that takes all those arguments at the same time:
function getElementsByIds(/* id1, id2, id3, ... */) {
var elements = {};
for (var i = 0; i < arguments.length; i++) {
elements[arguments[i]] = document.getElementById(arguments[i]);
}
return elements;
}
Then you would have references to all your elements stored in one object:
var el = getElementsByIds("myImage", "myDiv");
el.myImage.src = "test.gif";
But you would still have to list all those ids.
You could simplify it even more if you want all elements with ids:
function getElementsWithIds() {
var elements = {};
var elementList = document.querySelectorAll("[id]");
for (var i = 0; i < elementList.length; i++) {
elements[elementList[i].id] = elementList[i];
}
return elements;
}
But it would be pretty expensive to call this function if you have many elements.
So, theoretically, if you would use the with keyword you could write code like this:
with (getElementsByIds('myButton', 'myImage', 'myTextbox')) {
myButton.onclick = function() {
myImage.src = myTextbox.value;
};
}
But I don't want to promote the use of with. Probably there's a better way to do it.
Well, you could create a shorthand function, that's what I do.
function $(element) {
return document.getElementById(element);
}
and then when you wanted to get it, you just do
$('yourid')
Also, another useful trick that I found, is that if you want to get the value or innerHTML of an item ID, you can make functions like this:
function $val(el) {
return $(el).value;
}
function $inner(el) {
return $(el).innerHTML;
}
Hope you like it!
I actually made a kind of mini javascript library based on this whole idea.
Here it is.
If this is on your own site, consider using a library like jQuery to give you this and many other useful shorthands that also abstract away browser differences. Personally, if I wrote enough code to be bothered by the longhand, I would include jQuery.
In jQuery, the syntax would be $("#someid"). If you then want the actual DOM element and not the jQuery wrapper, it's $("#someid")[0], but you could most likely do whatever you're after with the jQuery wrapper.
Or, if you're using this in a browser developer console, research their built-in utilities. As someone else mentioned, the Chrome JavaScript console includes a $("someid") method, and you can also click an element in the developer tools "Elements" view and then reference it with $0 from the console. The previously selected element becomes $1 and so on.
If the only issue here is typing, maybe you should just get yourself a JavaScript editor with intellisense.
If the purpose is to get shorter code, then you could consider a JavaScript library like jQuery, or you can just write your own shorthand functions, like:
function byId(string) {return document.getElementById(string);}
I used to do the above for better performance. What I learnt last year is that with compression techniques the server does it automatically for you, so my shortening technique was actually making my code heavier. Now I am just happy with typing the whole document.getElementById.
If you are asking for a shorthand function...
<!DOCTYPE html>
<html>
<body>
The content of the body element is displayed in your browser.
<div id="d1">DIV</div>
<script>
var d=document;
d.g=document.getElementById;
d.g("d1").innerHTML = "catch";
</script>
</body>
</html>
or
<!DOCTYPE html>
<html>
<body>
The content of the body element is displayed in your browser.
<div id="d1">DIV</div>
<script>
var w=window;
w["d1"].innerHTML = "catch2";
</script>
</body>
Arrow functions make is shorter.
var $id = (id) => document.getElementById(id);
wrap the document.querySelectorAll ... a jquery like select
function $(selector){
var s = document.querySelectorAll(selector);
return s.length > 1 ? s : s[0];
}
// usage: $('$myId')
Well, if the id of the element does not compete with any properties of the global object, you don't have to use any function.
myDiv.appendChild(document.createTextNode("Once I was myDiv. "));
myDiv.id = "yourDiv";
yourDiv.appendChild(document.createTextNode("But now I'm yourDiv."));
edit: But you don't want to make use of this 'feature'.
Another wrapper:
const IDS = new Proxy({}, {
get: function(target, id) {
return document.getElementById(id); } });
IDS.camelCaseId.style.color = 'red';
IDS['dash-id'].style.color = 'blue';
<div id="camelCaseId">div 1</div>
<div id="dash-id">div 2</div>
This, in case you don't want to use the unthinkable, see above.
You can use a wrapper function like :
const byId = (id) => document.getElementById(id);
Or
Assign document.getElementById to a variable by binding it with document object.
const byId = document.getElementById.bind(document);
Note: In second approach, If you don't bind document.getElementById with document you'll get error :
Uncaught TypeError: Illegal invocation
What Function.bind does is it creates a new function with its this keyword set to value that you provide as argument to Function.bind.
Read docs for Function.bind
const $id = id => document.getElementById(id);
...
$id('header')
I just use: function id(id) {return document.getElementById(id);}", called by id(target id).action;
It works for me in Chrome, Edge & Firefox, though not in Safari or Opera.
I wrote this yesterday and found it quite useful.
function gid(id, attribute) {
var x = 'document.getElementById("'+id+'")';
if(attribute) x += '.'+attribute;
eval('x =' + x);
return x;
}
This is how you use it.
// Get element by ID
var node = gid('someID'); //returns <p id='someID' class='style'>Hello World</p>
// returns 'Hello World'
// var getText = document.GetElementById('someID').innerText;
var getText = gid('someID', 'innerText');
// Get parent node
var parentNode = gid('someID', 'parentNode');

Categories