RactiveJS Should Target The Element Triggering Proxy Event, Not It's Children - javascript

I'm trying to add an active classname to the li that is clicked on. Showing that it's selected.
My template:
var legCatagoryTemplate = "<ul>{{#legs:i}}<li><a href='#' on-click='selectCategory:{{this}},{{i}}' data-id='{{i}}'><figure><div class='imgWrapper'><img src='{{preview}}'></div><figcaption><h4>{{name}}</h4><p>W: {{width}}" H:<span></span>: {{material}}</p></figcaption></figure></a></li>{{/legs}}</ul>";
How its called:
var legCategoryView = new Ractive({
el: "#catalog",
template: legCatagoryTemplate,
data: response_from_ajax
});
How I'm handling the event:
legCategoryView.on('selectCategory', function ( event, self, index ){
console.log(event.target, self, index);
}
What I've found:
event.target is the element inside of the a that was clicked (eg div.imgwrapper, figcaption)
Non Ractive behaves similarly: click event on a div should not be triggered by it's children
What is a good solution to targeting the element with the on-click proxy object?

You might just traverse the DOM and find the li element but that can cause troubles in certain situations. If you call ractive.set('legs', new_data), Ractive will reuse the existing nodes, so your class will remain there. There are several solutions for this problem (the third is probably the best):
Use ractive.merge() instead of ractive.set().
Use splice() and push() instead of ractive.set().
Change your template and let Ractive manage the class:
<ul>
{{#legs:i}}
<li class="{{#.active }}active{{/}}">
<a href='#' on-click='selectCategory:{{this}},{{i}}' data-id='{{i}}'>
<figure>
<div class='imgWrapper'><img src='{{preview}}'></div>
<figcaption>
<h4>{{name}}</h4>
<p>W: {{width}}" H:<span></span>: {{material}}</p>
</figcaption>
</figure>
</a>
</li>
{{/legs}}
</ul>
ractive.on('selectCategory', function ( e ) {
ractive.set(e.keypath + '.active', true);
});

I just had the idea of using jQuery to traverse up the dom with $.closest() like this
legCategoryView.on('selectCategory', function ( event, self, index ){
$(event.original.target).closest('li').addClass("active");
}
That works actually.

Related

Removing a group of event listeners

I have a bunch of event listeners as illustrated below :
<a id="target0" >target0</a>
<div id="container">
<a id="target1" >target1</a>
<a id="target2" >target1</a>
<a id="target3" >target1</a>
.. more targets with event listeners
<script>
document.getElementById("#target1").addEventListener("click", ...);
document.getElementById("#target2").addEventListener("mouseover", ...);
document.getElementById("#target3").addEventListener("mouseout", ...);
...
</script>
</div>
<script>
document.getElementById("#target0").addEventListener("click", ...);
</script>
How do I write a function that will kill ALL DOM elements with event listeners binded to them ( with function reference or anonymous functions ) that are inside the container div ?
function killEvtListenersInsideContainerDiv(){
// loop to all DOM elements inside container that has event listeners and disable them
}
The container div is dynamically loaded by AJAX so the event listeners inside them will vary. The only one "static" is target0.
If you will have more than ont container you should add inside class for each a, something like this:
<div>
<a class="container1" id="#target1"></a>
...
</div>
To remove events there are two ways, I recommend the second because it easier and more modern:
1)Use native javascript, only if you can't use jquery for some technical reasons
var elements = document.getElementsByClassName("container1");
elements.forEach(function(elem, i, arr) {
element.removeEventListener("click");
}, this);
2)Use jquery:
//set
$('.container1').click(function() {
alert("click");
});
//remove
$('.container1').click(function(){}); //just empty function() :) the same as .click(null)
UPDATE: If you can't add class use this
javascript:
var elements = document.querySelector('#idDiv a'); //or '.classId a'
elements.forEach(function(elem, i, arr) {
element.removeEventListener("click");
}, this);
jquery
$('#idDiv a).click(function(){});
There is no way to check if event has been set before, if you really need you should implement this functionality with your hands(use flags for example). If function has already existed, new function will always override previous. In 99.9% cases it is enough.

Need to access the `id` of clicked `li` created inside `map` function

I am rendering 'rows' of job listings represented by li elements created by calling map on a prop that holds an array of job listings.
Each li has an onClick handler within which I need to access the id associated with the clicked row in order to show the detail page of the job listing with that id. Note that the map adds the id values of the job listings as the key properties of the li elements.
The map function looks something like this:
var jobListRow = this.props.joblists.map(function(jobrowobj) {
return (
<li key={jobrowobj.id} onClick={this.handleClick}>
<div>bla bla</div>
<span>bla bla</span>
</li>
);
});
And the onClick handler looks like this:
handleClick: function(event) {
console.log(event.target);
}
Inside the handler I hoped to use the target of the clicked li to get the value of the key property for that li, but the target returned seems to be different every time and happens to be a varying child element of the li. How can I access the key of the li that was clicked? Or, phrased differently: how can I access the id of the job listing that was clicked?
This should work, you need to have the right context binding:
var jobListRow = this.props.joblists.map(function(jobrowobj, i){
return(
<li key={jobrowobj.id} onClick={this.handleClick.bind(this,i)}>
<div>bla bla</div>
<span>bla bla</span>
</li>
)
});
handleClick: function(i) {
console.log('You clicked: ' + this.props.joblists[i]);
}
Found this implementation here: https://facebook.github.io/react/tips/communicate-between-components.html
It seems it fits your needs. Hope it helped
The map function has an option to pass 'this' as a second parameter. I do it this way.
var vm = this;
var jobListRow = this.props.joblists.map(function(jobrowobj){
return(
<li key={jobrowobj.id} id={jobrowobj.id} onClick={this.handleClick}>
<div>bla bla</div>
<span>bla bla</span>
</li>
)
}, vm);
handleClick: function(event) {
console.log('You clicked: ' + event.target.id);
}

JQuery contains not working multiple li

I´m trying to detect if an element it´s already appended into the DOM using $contains
var isAlreadyAppended = function (key) {
return ($.contains($('#allBellMessageId'), $("#" + key)));
};
var test= function(){
if(!isRepeated('a')){
//Append into DOM
};
if(!isRepeated('b')){
//Append into DOM
};
if(!isRepeated('c')){
//Append into DOM
};
if(!isRepeated('a')){
//Append into DOM
};
}
And adding the keys, a,b,c,a still is returning false, false, false, false.
And the html code generated is this one
<li id="bellMessagesId" class="dropdown notifications">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<ul id="allBellMessageId" class="dropdown-menu bellMessage-dropdown-menu">
<div class="col-xs-12">
<li id="a" class="col-xs-12">
<li id="b" class="col-xs-12">
<li id="c" class="col-xs-12">
<li id="a" class="col-xs-12">
</ul>
</li>
<li>
And the render is 4 messages, a,b,c,a.
What I´m doing wrong?.
Regards.
isRepeated()does not return anything, it just executes. Because its not returning anything, it will return undefined, so when you check it will say undefined and evaluate to false. So try this:
var isRepeated(key) = function(key){
return $.contains($('#fooElements'), $("#" + key))
};
Apart from that, I'm not very familiar with $.contains, so you could try the following:
var isRepeated(key) = function(key){
return $('#fooElements').find("#" + key).length ? true : false;
};
I use this all the time to check if something exists and it works like a charm.
From jQuery documentation
The first argument of .contains() must be a DOM element, not a jQuery object or plain JavaScript object. Try running a loop and using
$.contains($('#fooElements')[i], $("#" + key)[0])
or something like that.
I think your issue is one related to Event Delegation if you're looking for elements that have been appended to the DOM.
Anything after the DOM is loaded is not registered. So if you're appending an element to something, you'll have to check its parent element first.
From the docs:
$( "#list" ).on( "click", "a", function( event ) {
event.preventDefault();
console.log( $( this ).text() );
});
If the <a> tag was appended to the #list element, you first need to check the #list element and then check for the a tag.

deactivate ONE button/thumb of a list

I have a video array, and then a list of thumbs that correspond to the array. When you click on one of the buttons, it needs to be deactivated for the duration of the video. I have it so that after the first click, it deactivates the whole list
$('li', '.thumbs').on('touchend click', function() {
$("#myVid").on("loadeddata", function() {
$("#bigPic").addClass("move");
$("#MyT").fadeOut(750);
});
playVideo2( $(this).index() );
$('li', '.thumbs').unbind();
});
if each item in the list is set up like this:
<li rel='1' id="first">
<div style="top:0px;">
<img src="graphics/filler.png" alt="" width="280" height="128" />
</div>
</li>
with the id being different for each, can I just put the id instead of the .thumbs, and just have it unbind or turn off itself? I know this must be inefficient, but I'm not sure how else to do it. Do I use this() somehow? If so, how?
You could make use of a global variable to check which video is playing.
//init
var currentlyPlaying = undefined;
Within your event handling part you set this variable to the ID of the clicked button.
currentlyPlaying = $(this).attr('id');
With that setup you can check the variable before doing anything in your script.
if ( !(currentlyPlaying == $(this).attr('id')) ) {
// your script
}
Alternatively, you can use the ID to unbind as you suggested, of course.
$('li', '.thumbs #' + $(this).attr('id')).unbind();

Ordering a list of elements without losing the event handler

I have this list :
<ul>
<li id="6">
list 6: somethings
</li>
<li id="2">
list 2: somethings
</li>
<li id="4">
list 4: somethings
</li>
<li id="5">
list 5: somethings
</li>
<li id="0">
list 0: somethings
</li>
</ul>
and I'd like (with Javascript/jQuery) order these elements by the id (ASC) keeping the event handler for each element.
Is it possible? How can I do it?
You could just assign the ID's into an array and use sort():
var a = [];
$("ul li").attr('id',function(i,e){
a.push(e);
});
$.each(a.sort(),function(i,e){
$("#"+e).appendTo('ul');
});
You are never removing them from the list, just moving them around. Click handler stays intact:
http://jsfiddle.net/niklasvh/nVLqR/
This should work fine. Using detach preserves any events associated with the element. You then use a custom sort function to compare the id attributes of each li element.
var ul = $("ul");
var li = ul.children("li");
li.detach().sort(function(a, b) {
var compA = $(a).prop("id");
var compB = $(b).prop("id");
return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
});
ul.append(li);
See an example fiddle here. A click event is attached to the li with ID "6". After the list has been reordered, that click event is still handled.
I think to order them you'll had to remove and add them back into the DOM... and therefore you'll certainly lose the event handler. Are you in control of the handler, can you rebind or use live() instead?
The alternative would be to absolutely position the li elements and use the css position properties (top, right, bottom, left) to move them around, this will keep them in the same order in the DOM, but render them in your desired order.t
This example work for me:
var mylist = $('.item').detach().sort(function (a, b) {
return $(a).find(selector).html() > $(b).find(selector).html();
});
$("#container").html(mylist);
or if you want to sort with other informations:
var mylist = $('.item').detach().sort(function (a, b) {
return $(a).find(selector).attr('data-title')() > $(b).find(selector).attr('data-title');
});
$("#container").html(mylist);

Categories

Resources