How to call your own method using "this" on a DOM node - javascript

I would like to make something like this:
function alrtHtml() {
alert(this.innerHTML)
}
function myFunc(id) {
document.getElementById(id).alrtHtml()
}
<html>
<head>
<meta charset="UTF-8" />
<title>test</title>
</head>
<body>
<h1 id="h1">Hello world!</h1>
<input type="button" value="click" onclick="myFunc('h1')" >
</body>
</html>
The result should be an alert with the text "Hello world!" inside the h1 tag.
It is my goal to be able to do this without explicitly passing the element as an argument to alertHtml.

You generally don't want to extend native prototypes, and one way to create chainable methods without doing that, is to create your own method to get the elements, and then create another chainable method to alert the innerHTML, like most libraries do.
Probably the simplest example would be something like this
function getElement(selector) {
if (!(this instanceof getElement)) return new getElement(selector);
this.element = document.querySelector(selector);
return this;
}
getElement.prototype.alertHtml = function() {
alert(this.element.innerHTML);
return this;
}
function myFunc(id) {
getElement(id).alertHtml();
}
myFunc('#test');
<div id="test">TEST</div>
This way you're only extending your own objects, not native objects, and you can create any kind of chainable method to add to that.

What you're looking to do is add your function to the prototype of whatever type document.getElementById(id) returns.
In this case it's returning an Element, so in order to add your function to its prototype you would write the following code.
Element.prototype.alrtHtml = function() {
alert(this.innerHTML)
}

As another alternative, you could also pass the element right to alertHTML:
function alertHTML(el) {
alert(el.innerHTML)
}
function myFunc(id) {
var elArg = document.getElementById(id)
alertHTML(elArg)
// You could also write it like this:
/*
alertHTML(document.getElementById('h1'))
*/
}
<h1 id='h1'>Hello, world</h1>
<button onclick="myFunc('h1')">Button</button>
There's a plethora of reasons to do that, but the gist of it is to avoid having issues if somebody else creates a alertHTML method on the Element prototype.
EDIT: If you really want to use this, you might also like to learn about binding functions - funfunfunction made a good video on this here. Here's how that would work:
function alertHTML() {
alert(this.innerHTML)
}
function myFunc(id) {
var el = document.getElementById(id)
alertHTML.apply(el)
}
<h1 id='h1'>Hello, world</h1>
<button onclick="myFunc('h1')">Button</button>
apply runs whatever its function is with this as the first argument you pass to apply. (The rest of the arguments you pass to apply are passed directly to the function.)

Related

Can I make an element "unfindable" from the DOM by redefining the native functions?

Suppose I have the following structure:
<html>
<head></head>
<body>
<div id="myDiv"></div>
</body>
</html>
By redefining some native JavaScript functions, can I make myDiv unfindable?
For example, I can do:
window.HTMLDocument.prototype.getElementById = (function() {
var oldefinition = window.HTMLDocument.prototype.getElementById;
return function() {
var returnValue = oldefinition.apply(this, arguments);
if (returnValue && returnValue.id === 'myDiv') {
return oldefinition.call(this, 'blablabla');
} else {
return returnValue;
}
}
})();
and I can do the same for the other functions such as:
querySelector
querySelectorAll
getElementsByTagName
getElementsByClassName
etc.
This works, but the div is still available by calling:
document.body.children[0]
Then is there a way to make my div unfindable, that is, can I redefine the value of the children field?
Well, this is an attempt that seems to work okay - at least, with regards to document.body.children. I tested a variation of the following code on MDN's website to hide all script tags that are immediate children of the document body.
The way this works is we tell document.body to use a new property called children. We then return the original contents of children, minus the ones we don't want.
var oldchildren = document.body.children;
Object.defineProperty(document.body, 'children', {
get() {
var lst = [];
for (var item of oldchildren) {
if (!(item.tagName === "div" && item.id === 'myDiv'))
lst.push(item);
}
return lst;
}
});
Any code that references document.body.children after this code runs won't see the div. This code might cause other code on your site to misbehave.

how to fix the closure bug in my jQuery code

the page always alert "btn2" ,whenever I click btn1 or btn2. it's seem the problem caused the "click" closures.but I don't know how to fixed it.
thanks in advance.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>untitled</title>
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script>
(function(){
$.fn.test = function(){
F.init.apply(this,arguments)
};
var F ={
that:null,
init:function(){
F.that = this;
$(F.that).click(function(e) {
F.method();
});
},
method:function(){
var text =$(F.that).html();
alert(text);
}
}
})();
$(function(){
$("#btn1").test(); //alert btn2---bug
$("#btn2").test(); //alert btn2
});
</script>
</head>
<body>
<button id="btn1">btn1</button>
<button id="btn2">btn2</button>
</body>
</html>
You have referred to your object class F by that name from within the class.
Your line F.that = this is effectively therefore creating what would in other OO languages be considered a "static member" of the class, so both #btn2 and #btn1 ended up sharing the same that member.
Furthermore, your click handler is trying to call F.method() - in effect also a static method call.
You'll need to create a new object of type F each time you wish to wrap it around an element. Only then will you get a separate this for each element.
I suggest using an off-the-shelf jQuery plugin model such as http://jqueryboilerplate.com/ instead of trying to invent your own. See this extract from that code:
$.fn[ pluginName ] = function ( options ) {
return this.each(function() {
if ( !$.data( this, "plugin_" + pluginName ) ) {
$.data( this, "plugin_" + pluginName, new Plugin( this, options ) );
}
});
};
Note how it uses new Plugin to create the plugin instance, and then stores it on the element using $.data, but only the first time the plugin is invoked against each element.
1)F is a static Object
2)So F.that is also static
3)so
F.that = this
line will set 'this' to F.that.
4)first time you call
$("#btn1").test();
then F.that will be equal to $("#btn1");//this will be equal to $("#btn1")
5)Next time you call
$("#btn2").test();
then F.that will be equal to $("#btn2");//this will be equal to $("#btn2")
6) So finally to F.that you are setting $("#btn2")
7)hence the $(F.that).html(); is essentially $($("#btn2").html()) which is further same as $("#btn2").html()
8)Hence alert is showing "btn2"

Javascript : Assign a function and pass parameter at the same time

I have html buttons "classA","classB" and "classC" to which I assign the onclick handler function as follows..
var classA = document.getElementById('classA');
classA.onclick = filterByClassA;
var classB = document.getElementById('classB');
classB.onclick = filterByClassB;
var classC = document.getElementById('classC');
classC.onclick = filterByClassC;
These 3 functions do the same thing, only difference being the class.
So, can I have a single function assigned to these buttons, called with different parameters for each button. Something like below
var classA = document.getElementById('classA');
classA.onclick = filterByClass('classA');
var classB = document.getElementById('classB');
classB.onclick = filterByClass('classB');
var classC = document.getElementById('classC');
classC.onclick = filterByClass('classC');
I know this is a function call and not assignment and this is wrong but is there a way I can achieve this i.e. assign a function and pass parameter at the same time but not call it?
function filterByClass(className)
{
return function()
{
// Do something with className
console.log(className);
}
}
Bind can help you out here: Its called partial application.
Bind Syntax
fun.bind(thisArg[, arg1[, arg2[, ...]]])
1st param is scope of the function when it is called.
From 2nd you can pass any number of agruments. See the below code to know how it works.
Code:
var classA = document.getElementById('classA');
classA.onclick = filterByClass.bind(classA, 'classA');
var classB = document.getElementById('classB');
classB.onclick = filterByClass.bind(classB, 'classB');
var classC = document.getElementById('classC');
classC.onclick = filterByClass.bind(classC, 'classC');
function filterByClass(className, eventObject) {
console.log(this, className, eventObject);
}
Update:
Check out the Compatibility section in the above MDN link. You may need to use it, if you are going to use bind in non modern browsers.
i always try to keep the code as short as possible
so if your buttons are inside a container you can do that.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>atest</title>
<script>
window.onload=function(){
var c=document.getElementsByTagName('div')[0].childNodes,
l=c.length,
filterByClass=function(){
console.log(this.id);//this.id is the classA or whatever
};
while(l--){
c[l].onclick=filterByClass;
}
}
</script>
</head>
<body>
<div>
<button id="classA">A</button>
<button id="classB">B</button>
<button id="classC">C</button>
</div>
</body>
</html>
in this case
document.getElementsByTagName('div')[0]
returns the first div in the document
childNodes
give u the list of the buttons inside that div
the while function adds the onclick event with your function 'filterByClass'
inside filterByClass u can access the element by this and so return it's id with this.id

Access javascript object directly through DOM traversing with jQuery

I'm working on a simple client side interface where I have a jQuery object that I want to access directly when clicking on a hyperlink. Simplified code:
<div class="controls">
<div class="score">
<a class="button" href="/add">Add points!</a>
</div>
</div>
$(".controls").myControls();
$.fn.myControls = function() {
return $.extend(this, $.myControls).initialize();
}
$.myControls = {
initialize: function() {
this.scoreElement = $("div.score", this);
this.linkElement = $("a", this.scoreElement);
this.scoreElement.score = 0;
var _this = this;
this.linkElement.click(function() {
_this.clickHandler(this);
});
},
clickHandler: function(element) {
var scoreElement = $(element).parent();
scoreElement.score = 1;
}
}
Explanation: .controls element has .score element which doubles as a container for score information (this.scoreElement.score). When I click on a link within the .score element, I find the parent element, which is the same element in the DOM as this.scoreElement and try to set its score property to 1. Obviously, this won't work, as the local scoreElement.score property in the clickHandler method is "undefined".
So here's my question: is there a simple way to access my this.scoreElement object directly through traversing the DOM with jQuery?
Surely I can check if this.scoreElement == $(element).parent() in some way and then access the right property in my this.scoreElement object, but direct access would be more elegant and robust. Is this possible? Am I going at it the wrong way? Thanks!
PS: Ignore the fact I use parent() to find the scoreElement, I only use it to illustrate my problem. Unless it is part of the problem, in that case don't ignore :)
While it's certainly possible to use your own 'control-object' to store the related data, I usually prefer to rely on jQuery doing it - with .data() method, like this:
$(this.scoreElement).data('score', 0); // in initialize()
$(this).parent().data('score', 1); // in clickHandler()
This approach allows me to scale more easily, as I never have to fear 'overlapping' issues, using a single 'control' object rather than object-for-element.
I would think that if you used jQuery's proxy function for your click handler, you then could just go this.scoreElement inside of clickHandler and you wouldn't even need to traverse the DOM. Like this:
$.myControls = {
initialize: function() {
this.scoreElement = $("div.score", this);
this.linkElement = $("a", this.scoreElement);
this.scoreElement.score = 0;
this.linkElement.click($.proxy(this.clickHandler, this));
},
clickHandler: function(event) {
var element = event.target;
this.scoreElement.score = 1;
}
}
After progressive simplification (and storing the score slightly differently) I get the code below, in which scoreElement is discovered once per .controls div, then held in a closure to make it available to its corresponding click handler. You could alternatively use .closest() - see commented out line.
$.fn.myControls = function() {
return this.each(function() {
var scoreElement = $("div.score", $(this));
scoreElement.data('score', 0);
$("a", scoreElement).on('click', function() {
scoreElement.data('score', 1);//get scoreElement from closure formed by the outer "each" function.
//$(this).closest(".score").data('score', 1);//alternative to the line above, not requiring closure.
});
});
};
Call as in the question with:
$(".controls").myControls();
This is so trivial and unidimensional it doesn't really warrant, in its own right, a jQuery plugin. Unless there was some compelling reason for a plugin (eg. reuse or the need for closely related methods), then I would phrase it as follows :
$(".controls").each(function() {
var scoreElement = $("div.score", $(this));
scoreElement.data('score', 0);
$("a", scoreElement).on('click', function() {
scoreElement.data('score', 1);//get scoreElement from closure formed by the outer "each" function.
//$(this).closest(".score").data('score', 1);//alternative to line above, not requiring closure.
});
});
That's the same code with the plugin wrapper removed and attached directly to the same base jQuery object.
And if you really wanted, you could write the whole thing in three lines as follows:
$(".controls").find("div.score").data('score', 0).find("a.button").on('click', function() {
$(this).closest(".score").data('score', 1);
});

How to dodge a DRY issue when calling a function with a constant?

I am still new to Javascript. I need to attach a function to handle events on some of my HTML elements.
I am doing the following:
$('#iinp0').keyup(function(){keyReleased('iinp0');});
$('#iinp1').keyup(function(){keyReleased('iinp1');});
$('#iinp2').keyup(function(){keyReleased('iinp2');});
$('#iinp3').keyup(function(){keyReleased('iinp3');});
$('#iinp4').keyup(function(){keyReleased('iinp4');});
$('#iinp5').keyup(function(){keyReleased('iinp5');});
$('#iinp6').keyup(function(){keyReleased('iinp6');});
$('#iinp7').keyup(function(){keyReleased('iinp7');});
I was hoping I could apply the Don't Repeat Yourself (DRY) principle with the following:
for (i=0;i<=7;i++) {
var tmp = 'iinp' + i;
$('#'+tmp).keyup(function(){keyReleased(tmp);});
}
but keyReleased() is not called with the proper values.
Is there a solution to this issue? I mean is there a simple way to attach my functions having a constant parameter?
Why not simply this:
$('#iinp0, #iinp1, #iinp2, #iinp3, #iinp4, #iinp5, #iinp6, #iinp7').keyup(function()
{
keyReleased(this.id);
});
You could even replace that long selector with an attribute selector:
$('[id^=iinp]').keyup(function()
{
keyReleased(this.id);
});
which will select any element who's id starts with iinp.
Note: This selector is a tad slower than the pure ID selectors - but is much easier to read and maintain (if you could qualify it with a tag selector, it'll be a bit faster).
In your case this would be the best:
$('[id^="iinp"]').keyup(function()
{
keyReleased(this.id);
});
But you may like to hear the reason it doesn't work: it's because JavaScript binds the tmp var to the bigger scope.
The following code works because we are explicitly binding the current value of tmp to the new function being created:
for (i=0;i<=7;i++) {
var tmp = 'iinp' + i;
$("#"+tmp).keyup((function(xtmp){ return function(){keyReleased(xtmp);} })(tmp));
}
Don't use numbered ids.
Instead use a class.
$('.iinp').keyup(function() {
var index = $(this).index('.iinp');
keyReleased('iinp', index);
});
HTML
<input class="input" id="iinp0" />
<input class="input" id="iinp1" />
<input class="input" id="iinp2" />
JS
$(function(){
$('.input').keyup(function() {
keyReleased(this.id.replace('iinp', ''));
});
function keyReleased(key) {
console.log(key)
}
})
Assuming each one of your inputs have the same class, or are the same element type (like input), you can assign them all to the same function using a selector and the on() function, and pass the id of the element to the keyReleased() function:
Example HTML:
<div id="formData">
<input type="text" id="iinp0" \>
<input type="text" id="iinp1" \>
</div>
jQuery JavaScript:
$("#formData").on("keyup", "input", function() {
keyReleased($(this).attr('id'));
});
http://jsfiddle.net/4SKgU/
The rest of these answers will do what you want, but personally I'd go a step further to reduce the amount of anonymous functions being made (although, some approaches will not do that):
Use classes for your input
<input class="keyup" id="iinp01" />
Bind using class and non-anonymous event handler
(function ($) { // closure
$(function () { // on document ready
$("input.keyup").keyup(keyReleased);
});
function keyReleased(e) {
var id = this.id,
$input = $(this);
// Do whatever you want
}
})(jQuery);
Hopefully that will help. If you aren't familiar with closures, learn about them!
If possible, I'd also provide a parent element for context:
<div id="keyup-container"><!-- inputs here --></div>
$("#keyup-container input.keyup")
It will be more efficient (if you are worried about that).

Categories