_.extend(object, Backbone.Events);
object.on("myalert:one", function(msg) {
document.body.innerHTML+=("eve1 " + ' msg:= '+msg+ ' ;name:= '+this.name);
},context);
object.on("myalert:two", function(msg) {
document.body.innerHTML+=(" eve2 " + ' msg:= '+msg+ ' ;name:= '+this.name);
});
I want сall all events are tied to this object with mask alert:.
object.trigger("myalert", "param");
There isn't any namespacing in Backbone.Events but you could add your own. For example, something like this:
obj.trigger_matching = function(re) {
var args = [''].concat([].splice.call(arguments, 1));
for(name in this._events) {
if(!name.match(re))
continue;
args[0] = name;
this.trigger.apply(this, args);
}
};
would allow you to say obj.trigger_matching(/^myalert:/, 1, 2, 3) and The Right Thing would happen.
Demo: http://jsfiddle.net/ambiguous/p8p5R/
That will trigger multiple 'all' events (one for each this.trigger.apply) which may or may not be what you want. If it isn't then replace the this.trigger.apply call with a custom version of the standard trigger so that you can trigger at most one 'all' event.
Related
I am trying to make a quiz app and i want the score to update. The change event for radio button in not triggered on clicking next question.
https://codepen.io/abhilashn/pen/BRepQz
// JavaScript Document
var quiz = { "JS" : [
{
"id" : 1,
"question" : "Inside which HTML element do we put the JavaScript?",
"options" : [
{"a": "<script>",
"b":"<javascript>",
"c":"<scripting>",
"d":"<js>"}
],
"answer":"<script>",
},
{
"id" : 2,
"question" : "What is the correct JavaScript syntax to change the content of the HTML element below.",
"options" : [
{"a": " document.getElementById('demo').innerHTML = 'Hello World!';",
"b":" document.getElementByName('p').innerHTML = 'Hello World!';",
"c":" document.getElement('p').innerHTML = 'Hello World!';",
"d":" #demo.innerHTML = 'Hello World!';"}
],
"answer":" document.getElementById('demo').innerHTML = 'Hello World!';",
}
]
}
var quizApp = function() {
this.score = 0;
this.qno = 1;
this.currentque = 0;
var totalque = quiz.JS.length;
this.displayQuiz = function(cque) {
this.currentque = cque;
if(this.currentque < totalque) {
$("#qid").html(this.qno++);
$("#question").html(quiz.JS[this.currentque].question);
$("#question-options").html("");
for (var key in quiz.JS[this.currentque].options[0]) {
if (quiz.JS[this.currentque].options[0].hasOwnProperty(key)) {
//console.log(key + " -> " + quiz.JS[this.currentque].options[0][key]);
$("#question-options").append(
"<div class='form-check option-block'>" +
"<label class='form-check-label'>" +
"<input type='radio' class='form-check-input' name='option' id='q"+key+"' value='" + quiz.JS[this.currentque].options[0][key] + "'>" +
quiz.JS[this.currentque].options[0][key] +
"</label>"
);
}
}
} else {
return alert("Your score: " + this.score) ;
}
}
this.checkAnswer = function(option) {
var answer = quiz.JS[this.currentque].answer;
option = option.replace(/\</g,"<") //for <
option = option.replace(/\>/g,">") //for >
console.log(answer);
console.log(option);
if(option == quiz.JS[this.currentque].answer) {
this.score = this.score + 1;
console.log(this.score);
}
}
this.changeQuestion = function(cque) {
this.currentque = this.currentque + cque;
this.displayQuiz(this.currentque);
}
}
var jsq = new quizApp();
$(document).ready(function() {
jsq.displayQuiz(0);
$('input[type=radio][name=option]').change(function(e) {
e.preventDefault();
if (this.checked) {
jsq.checkAnswer(this.value);
}
});
});
$('#next').click(function(e) {
e.preventDefault();
jsq.changeQuestion(1);
});
You are only applying the event handler on the elements that are already present, and not on the one that will be created later.
The event handler should be on an element that is a parent to all the future elements.
Like this:
$('#question-options').on('change', 'input[type=radio][name=option]', function(e) {
// code
});
From jQuery documentation on on:
Event handlers are bound only to the currently selected elements; they must exist at the time your code makes the call to .on(). To ensure the elements are present and can be selected, [...] use delegated events to attach event handlers.
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers.
I'm trying to build my first plugin using jquery.
So far successful, but I'm stuck in deleting the notifications.
I was able to delete the notification on a click event.
Notification.prototype.destroy = function(element) {
var self = this;
element.closest('.notification-container').remove();
};
And I call that function inside init method.
Notification.prototype.init = function() {
var self = this;
self.$el.on('click', function() {
self.build();
});
self.$body.on('click', '.close', function() {
self.destroy(this);
})
};
Now I wanted to give a auto close option to the user, and I thought of using the setTimeout function, but as I've created the function passing the parameter as current element, I'm unable to get it.
Here's the pen.
Any help will be much appreciated.
Thanks!
You had several problems there:
The setTimeout function must be called upon display (and not upon build), otherwise it can be called even before you display the notification (hence your notification will not be automatically removed).
When you call the setTimeout in order to destroy the notification - you need to pass the container of the notification you just created, so the destroy function will be able to find the relevant element to remove (when you use the click option - you pass the X element, so it's easy to find the closest container, but when you use the setTimeout you must pass the container element himself).
I think all of the changes I made are in the build function, here it is:
Notification.prototype.build = function() {
var self = this;
var closeHTML = self.options.autoClose ? '' : '';
if (self.options.type == 'thumb') {
var $notificationHTML = $('<div class="notification-container">' +
'<i class="close">x</i>' +
'<div class="notification">' +
'<div class="thumb-container">' +
'<img src="' + self.options.src + '">' +
'</div>' +
'<p>' + self.options.text + '</p>' +
'</div>' +
'</div>');
} else {
var $notificationHTML = $('<div class="notification-container">' +
'<i class="close">x</i>' +
'<div class="notification ' + self.options.style + '">' +
'<p>' + self.options.text + '</p>' +
'</div>' +
'</div>');
}
self.$body.prepend($notificationHTML);
if(self.options.autoClose) {
setTimeout(function() {
self.destroy($notificationHTML);
}, 5000)
} else {
self.$body.on('click', '.close', function() {
self.destroy(this);
})
}
};
And a working codepen:
http://codepen.io/anon/pen/JKgPgB?editors=0010
I'm trying to use an array and for-loop to index and name some Howls and Howl trigger buttons.
I've referenced this question for what I'm trying to achieve: Howler - Random sound
The difference with mine is that it's without the random aspect plus I've added some method calls.
I'm adding the buttons used to trigger the Howls into the loop and that's where it seems to be failing - namely when the buttons are clicked.
Console reports the following when either button is clicked:
Uncaught TypeError: Cannot read property 'play' of undefined
Specifically referring to this: sounds[i].play(); or sounds[i].pause();
Here's the JS:
var sounds = ['sound1', 'sound2'];
var howls = {};
for (var i=0; i<sounds.length; i++) {
howls[sounds[i]] = new Howl({
urls: ['http://powellian.com/assets/audio/' + sounds[i] + '.mp3', 'http://powellian.com/assets/audio/' + sounds[i] + '.ogg'],
volume: 1,
onplay: function() {
console.log('Playing: ' + sounds[i]);
$(sounds[i]).removeClass('static').addClass('playing');
$(sounds[i] + ' span.ascii-play').addClass('hide');
$(sounds[i] + ' span.ascii-pause').removeClass('hide');
},
onpause: function() {
console.log('Paused: ' + sounds[i]);
$(sounds[i]).removeClass('playing').addClass('paused');
$(sounds[i] + ' span.ascii-play').removeClass('hide');
$(sounds[i] + ' span.ascii-pause').addClass('hide');
},
onend: function() {
console.log('Finished: ' + sounds[i]);
$(sounds[i]).removeClass().addClass('static');
$(sounds[i] + ' span.ascii-play').removeClass('hide');
$(sounds[i] + ' span.ascii-pause').addClass('hide');
}
});
// PLAY btn
$('#' + sounds[i] + ' span.ascii-play').on('click', function (e) {
sounds[i].play();
});
// PAUSE btn
$('#' + sounds[i] + ' span.ascii-pause').on('click', function (e) {
sounds[i].pause();
});
}
I had a non-array/for-loop version working fine with 2 Howls, and the add/remove class stuff works fine so please ignore that.
I will eventually be generating 16 Howls from the array.
Here's a fiddle which includes the markup structure: Howler Fiddle
Any help would be appreciated, thanks in advance.
There are two issues that I see here:
You are referencing the variable i inside the click handler without maintaining scope. Because of this, it will always see i as the last value. You could use bind as one way of fixing this:
$('#' + sounds[i] + ' span.ascii-play').on('click', function (i2, e) {
sounds[i2].play();
}.bind(null, i));
You are trying to call play on sounds, which isn't holding the reference to the Howl object. You should be calling play on howls[sounds[i2]] instead.
EDIT: In this case it is just easier to use a forEach, so I've updated your fiddle to do that and fix the scoping issues here: http://jsfiddle.net/zmjz7sf3/1/.
I'm using nvd3 for a multiBarHorizontalChart,i need guidance on how to disable tooltip for 0 values.
.tooltip(function(key, x, y, e) {
return '<h3>' + key + ' ' + e.point.label + '</h3>' + '<p>' + y + '</p>';
})
This function i am using to display tooltip value.
Unfortunately, I didn't find any non-hacky way to do this.
The not-so-elegant way would be to replace original handler for event tooltipShow with the custom one, that will check the value:
var originalTooltipHandler = chart.dispatch.on("tooltipShow");
chart.dispatch.on("tooltipShow", function (e) {
// check whatever you like here
if (e.value != 0) {
// and call original handler only when needed
originalTooltipHandler.apply(this, arguments);
}
});
But this is not enough if your chart is updated (like, you expect resize event or allow changing displayed data by clicking on legend, so almost in all cases), because when chart.update() is executed, it will restore original tooltipShow handler. To workaround that we have to override chart.update() too:
nv.addGraph(function () {
chart = nv.models.multiBarHorizontalChart()
// ... parameters setting here
// before data was set, `chart.update()` is not available
d3.select('#chart1 svg')
.datum(long_short_data)
.call(chart);
// now we can override
filterTooltips(chart, function (e) {
return e.value !== 0;
});
// ...
return chart;
function filterTooltips(chart, predicate) {
var originalUpdate = chart.update;
chart.update = function () {
// call to original `update()` will reset filter
originalUpdate.apply(this, arguments);
// set custom filter again
filterTooltips(chart, predicate);
};
var originalTooltipHandler = chart.dispatch.on("tooltipShow");
chart.dispatch.on("tooltipShow", function (e) {
if (predicate(e)) {
originalTooltipHandler.apply(this, arguments);
}
});
}
});
See live demo.
I am populating a table with an XML file, I have a column that links to more details. Because of the way I'm running the web page (Chrome extension) I need to dynamically add an event handler when the table is populated.
I have this working...
document.addEventListener('DOMContentLoaded', function () {
document.getElementById("detailLink").addEventListener('click',
clickHandlerDetailLink); });
function clickHandlerDetailLink(e) { detailLinkPress('SHOW'); }
function detailLinkPress(str) {
alert("Message that will show more detail");
}
But how do I go about adding the event handler dynamically? I have assigned all the fields in that column the id of detailLink.
You probably need to listen for a mutation event for the table, and then check each time the target element which has fired the event. Previously it used to be these events "DOMNodeInserted", or "DOMSubtreeModified", but they were very slow so according to new specifications the listener is called MutationObserver (which is much faster than the previous ones). This is an example from some Mozilla webpage edited for my testing :
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
alert(mutation.target.id + ", " + mutation.type +
(mutation.addedNodes ? ", added nodes(" + mutation.addedNodes.length + "): " + printNodeList(mutation.addedNodes) : "") +
(mutation.removedNodes ? ", removed nodes(" + mutation.removedNodes.length + "): " + printNodeList(mutation.removedNodes) : ""));
});
});
// configuration of the observer:
var config = { attributes: false, childList: true, characterData: false };
var element = document.getElementById('TestID');
// pass in the target node, as well as the observer options
observer.observe(element, config);
function printNodeList(nodelist)
{
if(!nodelist)
return "";
var i = 0;
var str = "";
for(; i < nodelist.length; ++i)
str += nodelist[i].textContent + ",";
return str;
}
If you want to assign an event to an element that doesn't yet exist, or to a series of elements (without creating one for each element), you need a delegate. A delegate is simply a parent element that will listen for the event instead of all the children. When it handles the event, you check to see if the element that threw the event is the one you're looking for.
If the parent <table> always exits, that would be a good place to add the listener. You can also add it to body. Also, you shouldn't be using detailLink as an id for more than one element. Use class instead.
Demo:
Script:
document.body.addEventListener( 'click', function ( event ) {
if( event.srcElement.className == 'detailLink' ) {
detailLinkPress( 'SHOW' );
};
} );
function detailLinkPress( str ) {
alert("Message that will show more detail");
};
HTML:
<div class="detailLink">click me</div>