I am using d3 and react to create charts. Clicking a button will update the chart. In the component for the chart is an onclick event listener, however the "this" keeps getting mixed up
One method with arrow functions returns Uncaught TypeError: node.getAttribute is not a function:
this.setupButtons= function() {
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', ()=> {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(this);
button.classed('active', true);
var buttonId = button.attr('id');
this.toggleDisplay(buttonId)
});
}
*/
This is of course because the this refers to the component instance. So I researched how to refer to the click event and found that using e or event.target is supposed to solve the issue. In react, however, an error is returned: Uncaught TypeError: Cannot read property 'target' of undefined
this.setupButtons= function() {
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', (e)=> {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(e.target);
button.classed('active', true);
var buttonId = button.attr('id');
this.toggleDisplay(buttonId)
});
}
Both this.setupButtons and this.toggleDisplay() are defined in the same method, which belongs to a component.
EDIT: This question does not appear to be a "duplicate" of the question provided.This was an issue of D3's handling of event, not the use of this, apparently. The solution to this question (add d3.event.target instead of event.target) is not provided as an answer in the question this supposedly duplicates.
You can store the reference of the outer this into another variable. I generally store it into a variable like var that = this and then you can reference the variable that wherever required.
this.setupButtons= function() {
var that = this;
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', ()=> {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(that);
button.classed('active', true);
var buttonId = button.attr('id');
that.toggleDisplay(buttonId)
});
}
d3 goes about things a bit differently. The event is accessed from d3 itself (d3.event).
Source: https://octoperf.com/blog/2018/04/17/d3-js-mouse-events-and-transitions-tutorial/#how-to-use-d3-event-object
Assign var that = this in the outer function. Now you have a way to reference the outer this.
this.setupButtons = function () {
var that = this;
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', function() {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(this);
button.classed('active', true);
var buttonId = button.attr('id');
that.toggleDisplay(buttonId)
});
}
Related
I have a few links. When I hover mouse over the links I would like to get the values stored in data attributes. I need to pick the t values and pass them into function
HTML
<a href="#" data-lat="23.333452" data-lon="-97.2234234">
JS
var loc = document.querySelectorAll("a[data-lat]");
loc.addEventListener("mouseover", locOver);
loc.addEventListener("mouseout", locOut);
function locOver() {
// do something
}
function locOut() {
// do something else
}
It's been a while since I used vanilla JS and it's been a long day so I'm sure it's pretty close but I'm stuck. I keep getting:
Uncaught TypeError: loc.addEventListener is not a function
What am I missing here?
You need to loop through the nodes that you obtained with document.querySelectorAll("a[data-lat]") for adding events.
Working example.
Node
<script>
var loc = document.querySelectorAll("a[data-lat]");
loc.forEach(node => {
node.addEventListener("mouseover", locOver);
node.addEventListener("mouseout", locOut);
})
function locOver(event) {
// do something
console.log('mouseover', event.target.dataset)
}
function locOut() {
// do something
console.log('locOut')
}
</script>
const loc = document.querySelector("a[data-lat]");
const locOver = () => {
console.log("Mouse is over the link");
}
const locOut = () => {
console.log("Mouse is out of the link");
}
loc.addEventListener("mouseover", locOver);
loc.addEventListener("mouseout", locOut);
Link
Explanation:
I target the link using .querySelector method that returns a single node.
After that i created two event handler for mouseOver and mouseOut and than i added the eventListener to the link.
I want to assign a JavaScript function to an event that already has a handler function. The new function should not change or remove (unassign) the existing function.
As an example:
I have a function called exFunction() that is already assigned to document.onmousemove. I have another function newFun() to assign to the same event. What I want to happen is when the document.onmousemove event occurs both functions are triggered.
newFun() is not a static function. It is going to be changing according to the webpage. (otherwise I can just write another function that calls both functions).
pure JavaScript only
By using addEventListener, you can apply multiple functions.
document.addEventListener('mousemove', exFunction)
document.addEventListener('mousemove', newFun)
You can also do like this
document.addEventListener("mousemove", function() {
myFunction();
someother();
});
As mentioned in other answers, you can easily assign several eventListeners to the event.
Note that the name of the function is the address of the code to execute and this address is immutable. The snippet shows what works and what doesn't.
var myDiv = document.getElementById('my-div');
function foo1(e) {
console.log('foo1');
if (e.altKey)
foo1 = foo2; //this doesn't work
};
function foo2(e) {
console.log('foo2');
if (e.shiftKey) {
myDiv.removeEventListener('click', foo2);
myDiv.addEventListener('click', foo3);
}
};
function foo3(e) {
console.log('foo3');
if (e.shiftKey) {
myDiv.removeEventListener('click', foo3);
myDiv.addEventListener('click', foo2);
}
};
//assign **addresses** of functions to eventListener
myDiv.addEventListener('click', foo1);
myDiv.addEventListener('click', foo2);
<div id="my-div" style="width:100px;height:100px;border:solid 1px"></div>
addEventListener is the right way to do this however time to time you may need to do this over the DOM element property event listeners by monkey patching.
Forgive me for partially snatching #AlexKudryashev's snippet.
var myDiv = document.getElementById('my-div'),
myBut = document.getElementById('my-button'),
f1 = e => console.log(`clicked "${e.currentTarget.id}" and f1 invoked`);
f2 = e => console.log(`clicked "${e.currentTarget.id}" and f2 invoked`);
monkeyPatch = (f1,f2) => e => (f1(e),f2(e));
myDiv.onclick = f1;
myBut.onclick = function(e){
console.log(`f2 monkey patched to f1. Click My Div to see the effect`);
myDiv.onclick = monkeyPatch(myDiv.onclick, f2);
}
<div id="my-div" style="width:100px;height:100px;border:solid 1px">My Div</div>
<button id="my-button">Monkey Patch f2</button>
I am trying to change a text according to a mouse hover event:
$(document).ready(function() {
$('.blog_post_container').hover(function() {
var title = $(this).find('.blog_title').text();
$(this).find('.blog_title').text("READ MORE >");
}, function() {
$(this).find('.blog_title').text(title);
}
});
In the HTML: div.blog_title is inside div.blog_post_container.
The variable "title" was created inside the function called on mouse hover to store the blog title of that specific blog post. The idea was to use it later, when the mouse leaves the blog_post_container, but I cannot use it outside the function where it was created.
I cannot either create the variable before that function, because it will always return the title of the first blog post.
I know the solution is simple, but I'm just not finding it. Any help is appreciated!
Store the title like this:
$(this).data('blog-title', title);
You can then retrieve it later with just a reference to the element:
var title = $('.blog_post_container').data('blog-title');
.data() API
Remove var to attach variable to window object
title = $(this).find('.blog_title').text();
It's better if you define it before the document ready
var title;
//document ready code
Maybe an overly complicated solution:
function rollOver(oElem, tHoverText){
this.tHover=tHoverText;
this.oElem=oElem;
this.init()
};
rollOver.prototype.init = function() {
this.oTitle = this.oElem.querySelector('.blog_title');
this.tTitle = this.oTitle.innerHTML;
this.initEvents();
};
rollOver.prototype.initEvents = function() {
var self = this;
this.oTitle.addEventListener('mousehover', this.changeTitle.bind(this), false);
this.oTitle.addEventListener('mouseout', this.restoreTitle.bind(this), false);
};
rollover.prototype.changeTitle = function() {
var self = this;
this.oTitle.innerHTML = self.tHover;
};
rollover.prototype.restoreTitle = function() {
var self = this;
this.oTitle.innerHTML = self.tTitle;
};
You can use it like this:
var blogPost = document.getElementsByClassName('blog_post_container')[0];
var b = new rollOver(blogPost, "Read more");
To retrieve the title text, use b.tTitle.
I'm trying to create a method that will copy the HTML from another part of the page and then set a click handler for the elements inside of it. Problem I'm having is that the handler is not firing when clicked.
Its funny because when I set the handler as an anonymous function, it fires when clicked, but when I set it to either self.initSizeSZ or myModule.initSizeSZ it won't fire.
// Elements are cached for performance.
var $selectSizes = $('#someSelector');
var $catalogWheels = $('#someOtherSelector');
var myModule = {
// This is the module that calls renderSizeSZ
loadWheels: function(){
if(!this.loaded){
this.renderSizesSZ();
this.loaded = true;
}
},
renderSizesSZ: function(){
var self = this;
var $clone = $selectSizes.clone(false);
var html = $selectSizes.html();
$catalogWheels
.find('#icf_catalog-sizes')
.html(html)
.find('li a')
// if this is set as an anonymous function, it will fire, but as a named function it won't
.click(self.initSizeSZ);
},
initSizeSZ: function(event){
console.log('firing')
}
}
I would like to convert the event handler to a jquery style click event but it doesnt seem to like passing the event through, perhaps its because its not an anonymous function anymore?
// variables
var faqOne = document.getElementById("faqOne");
var $hiddenOne = $(".faqOneHidden");
// javascript event handler works!
faqOne.addEventListener("click", function(e){
showFaqOne.showClickedFaq(e);
}, false);
// javascript event handle - doesnt work!
$("#faqOne").click(function(){
showFaqOne.showClickedFaq(e);
});
// constructor
function DisplayQFaqs(link, faq){
this.link = link;
this.faq = faq;
}
// method prototype
DisplayQFaqs.prototype.showClickedFaq = function(e){
var el = e.currentTarget;
if(el === this.link) {
this.faq.toggle("slow", function(){
});
}
};
// new DisplayQFaqs Objects
var showFaqOne = new DisplayQFaqs(faqOne,$hiddenOne);
Your e is undefined inside
$("#faqOne").click(function(){
showFaqOne.showClickedFaq(e);
});
Change it to
$("#faqOne").click(function(e){//Now e is there
showFaqOne.showClickedFaq(e);
});