How to get boolean output from an event handler - javascript

I'm trying to check if a 'tr' element has a nextSilbing element, using a 'DOMNodeInserted' event that will detect a DOM change whenever a tr element was added into a table element after it's inserted through a form like below:
I tried to get a boolean output from the event handler but I didn't succeed, so I tried to create a function to get that output but this was impossible unless I wrapped the statement with a console.log:
function findIdx(el) {
console.log(document.querySelector('tr:first-of-type').nextSibling === null);
}
$('tbody').on('DOMNodeInserted', function(e) {
let a = $(e.target)
findIdx(a);
})

You can cast any value to boolean type with the use of !!
const anyValidValue = 1;
console.log('valiud: ', !!anyValidValue);
const notValidValue = null;
console.log('not valiud: ', !!notValidValue);
so I belive your function can look like this:
function findIdx(el) {
return !!document.querySelector('tr:first-of-type').nextSibling;
}

Related

Is there a way I can dynamically bind a string and the text it outputs without using setInterval?

Is there a way I can dynamically bind a string and the text it outputs without using setInterval? I want it to be similar to Angular and Vue though I want to do this with vanilla JS. I want to be able to open the console and change the value at any time and see the change output on my element. Thank you in advance!
I think your only two options are:
A. Edit the element directly, e.g.
myPublicElemeVariable.innerText = 'Bla'
B. Use a setter (or Proxy):
obj = {
get str() { return this.myStr; }
set str(val) {
elem.innerText = val;
this.myStr = val;
}
}
C. Just use a function/method!
If you mean you want change to be event-driven, there is already a very simple event framework in javascript - the EventTarget class as demonstrated by this Code Sandbox
//define a watchable thing
class ValueTarget extends EventTarget {
constructor(value = "") {
super();
this.setValue(value);
}
getValue() {
return this._value;
}
setValue(value) {
this._value = value;
this.dispatchEvent(new CustomEvent("change", { detail: { value } }));
}
}
//get the page elements
const inputElement = document.querySelector("input");
const outputElement = document.querySelector("h1");
//create a watchable thing
const fieldTarget = new ValueTarget("");
//wire events that will change the watchable
inputElement.addEventListener("input", function (e) {
fieldTarget.setValue(e.target.value);
});
//receive notifications from the watchable
fieldTarget.addEventListener("change", (e) => {
outputElement.textContent = e.detail.value;
});
You may be as well to build your own given how simple it is - maintains a list of listeners and calls them when notified. My work recently needed such a thing which I knocked up in Typescript at https://github.com/cefn/lauf/blob/main/modules/lauf-store/src/core/watchable.ts#L3-L21 and would therefore be very easy to redo in javascript.

Get element event listener and store it

I want to remove an event listener from an element and later assign it back. Is there a way to get it and store in a variable smth like var storedListener = Element.getEventListener('click'), so that later I can do something like Element.addEventListener('click', storedListener)?
UPDATE
Listener is assigned inside template, I use Angular 2. It's
<div *ngFor="let entry of data; let i=index">
<div class="element-description" (click)="editElementDescription(i)">{{entry.description}}</div>
</div>
What I want to do is to make contents of inside <div> an <input> after I click it, so that I can change value and send it to server. And inside editElementDescription() I do the following:
public editElementDescription(index: number): void {
var elementDescription: HTMLDivElement = <HTMLDivElement>document.getElementsByClassName('element-description')[index];
elementDescription.removeEventListener('click');
elementDescription.innerHTML = '<input id="change-description-input" type="text" value="' + elementDescription.innerHTML + '"/>';
}
I remove that click listener because otherwise contents of <input> will get that innerHTML if I click it one more time. So the idea is to assign that <input> element a change listener, which will replace <input> by it's value and bring the parent <div> it's original listener back.
In order to remove a listener added with .addEventListener(), you must keep track of the listener function reference and remove it later with .removeEventListener().
Something like that:
var btn = document.getElementById('btn');
var btn_add = document.getElementById('btn-add-listener');
var btn_remove = document.getElementById('btn-remove-listener');
var fnListener = function(e) {
alert('Clicked!');
};
btn_add.addEventListener('click', function() {
btn.addEventListener('click', fnListener);
});
btn_remove.addEventListener('click', function() {
btn.removeEventListener('click', fnListener);
});
Working demo: https://jsfiddle.net/mrlew/k5m1nog3/
Other approach (added after question update)
Considering your actual problem, I suggest another approach: instead of handle events, you can set a data- attribute in the element indicating it's open. Then you just modify your inner HTML if the attribute is not present.
Something like this:
function editElementDescription(index) {
var elementDescription = document.getElementsByClassName('element-description')[index];
var isOpen = elementDescription.getAttribute('data-isOpen');
if (!isOpen) {
elementDescription.setAttribute('data-isOpen', 'true');
elementDescription.innerHTML = '<input id="change-description-input" type="text" value="' + elementDescription.innerHTML + '"/>';
}
}
Working demo: https://jsfiddle.net/mrlew/e0yrL08v/
You are getting it a bit wrong... there is no list of event listeners accessible from JavaScript... the only thing you can do is to remove/add an event if you know the origin.
There are 2 functions to manipulate event listeners:
addEventListener(event_type,function);
removeEventListener(event_type,function);
One object can have multiple events of same type... the only way to distinguish them is by giving an exact function being called.
Please note that if it's jQuery, it is possible, as it has own event stack... example below:
var events = $("#object1").data('events');
var $object2 = $("#object2");
if (events) {
for(var eventType in events){
for(var idx in events[eventType]){
$object2[eventType](events[eventType][idx].handler);
}
$('#object1').off(eventType);
}
}
No, this isn't possible since .getEventListener is only available for debugging purposes.
There is unfortunately no way in standard JavaScript to programmatically get back the EventListeners attached to an object, and any library that tries to accomplish this will rely on unstable non-standard interfaces that may be discontinued any day.
So if your goal was to manipulate the listeners added by a library you have no control over, you're out of luck.
On the other hand if you control the environment then you can store a reference to the attached callback if you want to attach the same listener to multiple objects, or remove it afterwards with .removeEventListener.
You could actually monkey-patch EventTarget.prototype.addEventListener to support this before anything else runs on your page, that wouldn't be a very clean solution to whatever problem you are having but if you think you really need to, here is a quick imperfect implementation of it (doesn't support useCapture argument):
// getEventListener polyfill, run this before anything else on your page.
(function monkeyPatchGetEventListeners(EventTarget) {
const eventListeners = new WeakMap();
const origAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function patchedAddEventListener(eventType, listener, ...args) {
let allListeners;
if (eventListeners.has(this)) {
allListeners = eventListeners.get(this);
} else {
allListeners = new Map();
eventListeners.set(this, allListeners);
}
let listeners;
if (allListeners.has(eventType)) {
listeners = allListeners.get(eventType);
} else {
listeners = [];
allListeners.set(eventType, listeners);
}
listeners.push(listener);
return origAddEventListener.call(this, eventType, listener,...args);
}
const origRemoveEventListener = EventTarget.prototype.removeEventListener;
EventTarget.prototype.removeEventListener = function patchedRemoveEventListener(eventType, listener, ...args) {
const call = () => origRemoveEventListener(eventType, listener, useCapture, ...args);
const allListeners = eventListeners.get(this);
if (!allListeners) { return call(); }
const listeners = allListeners.get(this);
if (!listeners) { return call(); }
const index = listeners.indexOf(listener);
if (index === -1) { return call(); }
index.splice(index, 1);
return call();
}
EventTarget.prototype.getEventListeners = function patchedGetEventListeners(eventType) {
const allListeners = eventListeners.get(this);
return allListeners && allListeners.get(eventType);
}
})(EventTarget);

How to pass int via eventListener in Javascript?

I generate a list of Magic cards from a Json file.
for (i in set.cards) {
leftCardElement = document.createElement("p");
leftCardElement.innerHTML = set.cards[i].points + " " + set.cards[i].name + "&nbsp";
leftCardElement.addEventListener("click", leftCardClicked);
}
function leftCardClicked(event) {// access card.cost and other things }
The problem is that when the leftCardElementClicked() method is called I'd like to be able to have access to the set.cards[i] object. The this object seems to refer to the element that I clicked on inside of leftCardElementClicked(). Is there some way that I can pass the index i to the event handler so I can get my card object?
Same like #AlejandroC but without the dependency on the i (this i will change on each iteration) ,
therefore I clone it before passing, using with Object.assign()
In your for loop do:
leftCardElement.addEventListener("click", leftCardClicked.bind(this, Object.assign({},set.cards[i]));
then your function should be:
function leftCardClicked(card, event) { ... }
In your for loop do:
leftCardElement.addEventListener("click", leftCardClicked.bind(this, set.cards[i]);
then your function should be:
function leftCardClicked(card, event) { ... }
Instead of attaching leftCardClicked directly as your event handler, instead use an anonymous function:
leftCardElement.addEventListener("click", function(evt) {
leftCardClicked(evt,i);
});
where i is your loop index. This creates a closure that passes that value of i to that instance of the click handler.

How to use jQuery events on non-DOM-related objects and their properties?

I need to use jQuery events on non-DOM-related objects.
This works fine:
var o = {
}
$(o).on('bump', function () {
alert('ouch')
})
$(o).trigger('bump')
http://jsfiddle.net/d35bf35y/
But instead I need to attach an event on a property... the following code does not work.
var o = {
prop: 'test'
}
// Bind an event handler
$(o.prop).on('bump', function () {
alert('ouch')
})
// Trigger an event
$(o.prop).trigger('bump')
http://jsfiddle.net/d35bf35y/1/
In my real application that property will have an object.
I would like to know if is possible use jQuery in this way or an alternative solution.
"In my real application that property will have an object."
That part is rather important. This works:
var o = {
prop: {}
}
// Bind an event handler
$(o.prop).on('bump', function () {
alert('ouch')
})
// Trigger an event
$(o.prop).trigger('bump')
You can attach one on the Object and everytime it fires, you run a function that does something with your properties. (check if they're the same, or else)
var o = {
prop: 'test'
};
$(o).on('bump', function (e) {
var props = e.delegateTarget;
// do what you gotta do, like check if something has changed from before.
})
$(o).trigger('bump')
Or if your property is also an object, it should work.

Trigger function from object using dynamic key

I'm struggling on triggering a function which is defined in an object, by pointing to it using a dynamic key. My code looks something like this:
$(function(){
var events = {
Test : function(){
console.log ('init');
}
}
$('#trigger').click(function(){
var e = $(this).data('event');
events[e];
});
});
So basically, there is some element #trigger with an attribute data-event="Test" (or something else), once it gets clicked I check the events object if there is a function defined for that trigger and fire it. However, it doesn't work this way. I can console log the events object but the function isn't executed. What am I doing wrong?
Thanks!
Invoke the function with ()
$('#trigger').click(function(){
var e = $(this).data('event');
events[e]();
// ^^this
});
DEMO

Categories