Reputation: 23
I'm JavaScript beginner and am still grasping some concepts around it, so sorry if the question is dumb or it's already answered, but I didn't know what to search for, as the language terminology is still kinda foreign to me (both JavaScript and English). Currently I'm trying to master "this" keyword and trying to minimize JS code inside HTML file and move as much as possible to external files.
This is my question:
Let's say I want to change paragraph's value from Hello World!
to foo bar
by clicking on paragraph itself, just by using "this" keyword and some JavaScript.
I can do it in external file like:
<!--index.html-->
<p onclick="setParagraphText(this, 'foo bar')">Hello World!</p>
----------------------------------------------
//script.js
function setParagraphText(paragraph, value) {
return paragraph.innerHTML = value;
}
Or inline, inside tag:
<p onclick="this.innerHTML='foo bar'">Hello World!</p>
My question is: is it possible to do a combination of these 2 ways, so that the p
value is not passed as an argument, but instead the function is invoked on it as an object (as a similar to 2nd example), but still keep the method of doing it in external file (like in 1st example)?
Something along the lines of this.function(value)
instead of function(this, value)
<!--index.html-->
<p onclick="this.setParagraphText('foo bar')">Hello World!</p>
----------------------------------------------
//script.js
function setParagraphText(value) {
//something with innerHTML = value; or whatever will work
}
Thanks in advance!
Upvotes: 2
Views: 252
Reputation: 5112
First of all, if you want to be able to call your function on this
, you have to know what the keyword this
here represents. One simple way to do it is to console.log it.
So, this
is the DOM element you have your inline javascript on ! Confirmed here: this in an inline event handler. Okay, if you want to have more information, console.log paragraph.constructor.
So, it's a HTMLParagraphElement
. That's what you would get if you call this:
document.createElement("p");
So, if you want to be able to call this.setParagraphText
, it comes down to calling setParagraphText
on an HTMLParagraphElement object. But in order to do that, HTMLParagraphElement has to implement it and one way to do this, as subarachnid suggested, is to add the function to its prototype so that it is shared by all instances of it. If you think this would be useful, take a look at Web Components.
Here is a link: Extending native HTML elements.
Basically, you do it like this (and the cool thing here is the functionality, that is changing its content when clicking on it, will be encapsulated within the class):
<!DOCTYPE html>
<html>
<body>
<script>
// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class CoolParagraph extends HTMLParagraphElement {
constructor() {
super();
this.addEventListener('click', e => this.setParagraphText('new value'));
}
setParagraphText(v) {
this.innerHTML = v;
}
}
customElements.define('cool-paragraph', CoolParagraph, {extends: 'p'});
</script>
<!-- This <p> is a cool paragraph. -->
<p is="cool-paragraph">Cool paragraph! Click on me and the content will change!</p>
</body>
</html>
So you don't even have to write inline Javascript anymore!
But if you want to have it your way and add your inline javascript, it's fine.
<!-- Note the this.setParagraphText(), now it works! -->
<p is="cool-paragraph" onclick="this.setParagraphText('foo bar')">Cool paragraph! Click on me and the content will change!</p>
<!DOCTYPE html>
<html>
<body>
<script>
// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class CoolParagraph extends HTMLParagraphElement {
constructor() {
super();
//this.addEventListener('click', e => this.setParagraphText('new value'));
}
setParagraphText(v) {
this.innerHTML = v;
}
}
customElements.define('cool-paragraph', CoolParagraph, {extends: 'p'});
</script>
<!-- This <p> is a cool paragraph. -->
<p is="cool-paragraph" onclick="this.setParagraphText('foo bar')">Cool paragraph! Click on me and the content will change!</p>
</body>
</html>
I don't know if that answers your question, but that should hopefully points you in the right direction.
Upvotes: 2
Reputation: 7591
Dunno if this is what you're after, but this
in the function is referring to the function itself, but you can send in a scope with .call(this, arguments)
which means that referring to this
in the function is actually the scope of the HTML element.
I'm showing two ways of how you can handle this, with either a data attribute or sending in a new value as a parameter.
function setParagraphText(newValue) {
this.innerText = this.dataset.text + " + " + newValue;
}
<p data-text="foo bar" onclick="setParagraphText.call(this, 'foo yah')">Hello World!</p>
Also read: Javascript call() & apply() vs bind()?
Upvotes: 0
Reputation: 181805
Currently I'm trying to [...] minimize JS code inside HTML file and move as much as possible to external files.
Then how about having no JS code inside your HTML? This lets you kill two birds with one stone:
document.getElementById("clickme").addEventListener("click", function() {
this.innerHTML = "foo bar"
})
<p id="clickme">Hello world!</p>
The listener you add with addEventListener
will be invoked with this
being the element that the listener was added on.
Upvotes: 0
Reputation: 3496
For your code to work you would have to enhance the HTMLElement prototype with your setParagraphText method (which is basically just a wrapper for this.innerHTML = value)
:
HTMLElement.prototype.setParagraphText = function(value) {
this.innerHTML = value;
};
Now something like this should work:
<p onclick="this.setParagraphText('foo bar')">Hello World!</p>
But I would strongly advise against modifiying native prototypes (older browsers like IE 9 don't even allow such a thing afaik).
Upvotes: 0
Reputation: 50759
You can add a data-
attribute to your p
tag which would store the data you want your text to change to (ie: "foo bar"
), and then use addEventListener
to add a click event-listener to paragraph tags which have this particular data-
attribute. By doing this, you're handing over the javascript logic to the javascript file, and thus limiting the JS written within your HTML file.
See example below:
const clickableElements = document.querySelectorAll('[data-click]');
clickableElements.forEach(elem => {
elem.addEventListener('click', function() {
const value = this.dataset.click;
this.innerHTML = value;
});
});
<p data-click="foo bar">Hello World!</p>
<p data-click="baz">Hello Moon!</p>
Upvotes: 2