I was wondering how I can capture events which occur from a child element from the parent as shown below. I know this method works to capture events using this but I need it from the child element.
I don't want to capture the event from the li and transfer information to foo through global variables or other such means because I'll ultimately be implementing a mousemove event which sends the position of the mouse per trigger, and it would be extremely inefficient sending this through the global variables technique (like as demonstrated in the API here) and slow down the app.
<polymer-element name="foo">
<template>
<ul>
<li>This is my child element</li>
</ul>
</template>
<script>
Polymer('foo', {
ready: function() {
document.querySelector('li').addEventListener('mousedown', function() {
console.log('mousedown event working');
})
}
});
</script>
</polymer-element>
Thanks in advance for any insight you may provide.
Don't use document.querySelector() because your li element is inside the shadow dom of the Polymer element. Btw. the element name is incorrect, it must contain a dash.
You can use the automatic node finding feature of Polymer to get the li element. Assign an id to the element and use the this.$ property (alternatively if you don't want to assign an id, you can use this.shadowRoot.querySelector()):
<li id="item">This is my child element</li>
this.$.item.addEventListener(...);
But with Polymer you can declaratively assign the callback function to the li element, which results in a much more readable code:
<polymer-element name="my-foo">
<template>
<ul>
<li on-mousedown="{{mousedown}}">This is my child element</li>
</ul>
</template>
<script>
Polymer('my-foo', {
mousedown: function() {
console.log('mousedown event working');
}
});
</script>
</polymer-element>
Related
I have the following markup
<ol>
<li class="ListItem">
<span class="sub">#qItem.CategoryText</span>
<input type="image" src="http://icons.iconarchive.com/icons/tatice/just-bins/256/bin-red-full-icon.png" width="30" class="deleteIcon" name="QuestionId" value="#qItem.Id" />
</li>
</ol>
and the following script
$(".ListItem").click(function(){
doActionA();
});
$(".deleteIcon").click(function(){
doActionB();
});
When I click in image, it also triggers click of ListItem. I understand this is because image is inside ListItem. but I want ListItem's click not to be fired when image is clicked.
Is there any way to do that ?
$(".deleteIcon").click(function(e){
doActionB();
e.stopPropagation();
});
You need to use event.stopPropagation() to prevents the event from bubbling up the DOM tree.
$(".deleteIcon").click(function(event){
event.stopPropagation()
doActionA();
});
The event you binded with delete icon is firing the parent event binding with ListItem, so you need to stop propagation for parent event when child is source of event.
$(document).on("click", ".deleteIcon", function() {
doActionB();
});
This method is the only one I found to work with nested elements, especially those generated by a library such as Pivottable (https://github.com/nicolaskruchten/pivottable)
$(".deleteIcon").click(function(){
doActionA();
return false;
});
Use .one(), as directed in the JQuery documentation.
Polymer 1.*
I had to write my own dropdown menu. I need to close the menu when the user clicks outside of the element. However, I am not able to catch the event when a user clicks outside of the element so I can close the menu.
Any ideas what I am doing wrong?
EDIT: I've studying paper-menu-button which closes paper-listbox when I click outside the element.... but I don't see anywhere where it catches that event https://github.com/PolymerElements/paper-menu-button/blob/master/paper-menu-button.js#L311
<dom-module id="sp-referrals-reservations-dropdown">
<template>
<style include="grid-dropdown-styles">
</style>
<div id="dropdown" class="grid-dropdown">
<paper-listbox>
<div class="grid-dropdown-item">Convert to stay</div>
<div class="grid-dropdown-item">Cancel reservation</div>
<div class="grid-dropdown-item">Delete reservation</div>
</paper-listbox>
</div>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'sp-referrals-reservations-dropdown',
behaviors: [Polymer.IronControlState],
properties: {
},
listeners: {
'tap': '_close',
'click': '_close',
'blur': '_close',
'focusout': '_close',
'focusChanged': '_close',
'focus-changed': '_close',
'active-changed': '_close',
'activeChanged': '_close',
'iron-activate': '_close',
'ironActivate': '_close',
},
open: function(e) {
},
_close: function() {
console.log('aaa');
this.$.dropdown.style.display = "none";
},
});
})();
</script>
</dom-module>
I am not sure, will it be enough, but if you wrap the sp-referrals-reservations-dropdown element with a parent-element then you can listen to parent-element events same as its child.
<parent-element></parent-element>
<dom-module id="parent-element">
<template>
<style>
:host {
display:block;
background:green;
width:100%;
height:100vh; }
</style>
<sp-referrals-reservations-dropdown id="spref"></sp-referrals-reservations-dropdown>
At parent's script:
Polymer({is:'parent-element', properties:{},
listeners:{ 'tap': '_taped'},
_taped:function(t){
this.$.spref._close();
}
});
this _taped functions will call child's _close function. Hope its help.
Incase of needed more. We can develop this.
Demo
EDIT
Wrap your element into paper-dialog. And at ready:function() call
this.$.dialog.open()
Then when you click outside of the element. paper-dialog will close automatically.
Just FYI, you weren't able to get this to work because custom elements don't listen for events outside of their own encapsulation unless you explicitly wire them up to do so... and if you do so, you can't use Polymer's built-in event handling.
So something like this would work:
// Insert this somewhere it'll get run once attached to the DOM
// This line keeps clicks within your element from closing the dropdown
this.shadowroot.addEventListener('click', (event) => { event.stopPropagation(); });
// And this listens for any click events that made it up to body, and closes the element
document.querySelector('body').addEventListener('click', this._close);
Or, at least, that's what I think was going on. The polymer elements either did it by binding to a different event (blur?) or by having the parent element trigger an event on click that told the child element to close.
My dom repeat displays a list of icons which I can bookmark or unbookmark ,which generating dom-repeat I call a function to find if this icon is bookmarked or not,that will return CSS class
.book-marked {
color: red;
}
.not-book-marked {
color: green;
}
<template is="dom-repeat" items="{{membersList}}">
<iron-icon icon="bookmark" class$="[[_computeBookMark(item.userId)]]" on-tap="_toogleBookMark"></iron-icon>
</template>
Once I get all my list of icon now if user click that icon I need to toogle css class.so I wrote on-tap function
_toogleBookMark:function(e) {
var userData = e.model.item; //gets entire data object of that element
var index = e.model.index; //gets index of that element
},
I can't use ID since its dom-repeat ,Is there any other ways so that I can change CSS of that dom-repeat element in _toogleBookMark() function on clicking? or is it possible to change CSS with index??or using "e" reference in _toogleBookMark(e) function !!
Not sure if I understood correctly - you want to access the element you've tapped?
Just use the event.target property then. It will return the element on which the event happened, in this case, the icon you have tapped.
_toogleBookMark = function(e) {
e.target.classList.toggle("not-book-marked");
}
Check this example.
Mind you:
1) When using Shady DOM, assuming our element is a custom element, target can be a component from the element's template, not the element itself. To prevent that, use Polymer.dom(e).localTarget (read more here).
2) When using a custom element with light DOM children, the above may not be enough, your (local)target will be a light DOM child, not the element you wished for. In that case, use Element.closest(selector) to (optionally) go up the DOM to the element you want. Read more about the method here.
As you just want to swap your class on tap, do it like this:
Add an own attribute, like data-id="1" and the id attribute, but be sure they have the same value:
<template is="dom-repeat" items="{{membersList}}">
<iron-icon icon="bookmark" class$="[[_computeBookMark(item.userId)]]" on-tap="_toogleBookMark" data-id="{{item.userId}}" id="{{item.userId}}"></iron-icon>
</template>
Now, inside your _toggleBookMark function, you can access the current tapped element and swap CSS classes by:
_toogleBookMark:function(e) {
// this gives you your userId from the data-id attribute
var userId = e.path[0].dataId;
// we can access the element now with
var element = this.$$('#' + e.path[0].dataId);
if (element.classList.contains('book-marked')) {
element.classList.remove('book-marked');
element.classList.add('not-book-marked');
} else {
element.classList.add('book-marked');
element.classList.remove('not-book-marked');
}
},
I'd like to add tabindex to all form elements. The form is dynamic and I can't add it to the HTML. I would like to run it as a function.
If there are several radio buttons with the same name, each must have it's own tabindex value. Most of the form elements on page start as <input>, except <select>. How do I account for that?
I guess I will need to run a loop and add the attribute, right?
var n = 1;
$('input, select').each(function() {
$(this).attr('tabindex', n++);
});
Strange question, but yes that's the basic idea:
$(":input:not(:hidden)").each(function (i) { $(this).attr('tabindex', i + 1); });
This uses :input to get everything including buttons and text areas. :not(:hidden) will just exclude the hidden inputs to avoid unnecessary tabs.
Might be better to avoid n++ to set different tabindex numbers.
Instead, try setting tabindex to 0:
$(':input:visible').each(function() {
$(this).attr('tabindex', '0');
});
tabindex="0" means that the element should be focusable in sequential keyboard navigation, but its order is defined by the document's source order.
~ developer.mozilla.org
The :input selector basically selects all form controls.
The :visible selector basically selects all elements that are visible.
or as suggested in the comments, if you have no other changes to apply to each visible input, then this should be enough:
$(':input:visible').attr('tabindex', '0');
Here, I described how can add aria-selected and tabindex value dynamically via jquery. I also want to see that how accessibility work with tablist, tab, and tabpanel role and how aria attributes work.Hope helps this code :
var $tabs = $('.tabs');
var $panels = $('.panel');
$tabs.on('click', 'a', function (e) {
e.preventDefault();
var id = $(this).attr('href');
// Find the currently visible tab and panel and hide them
$tabs.find('[aria-selected="true"]').attr({
'aria-selected': false,
'tabindex': -1
});
$(this).attr({
'aria-selected': true,
'tabindex': 0
});
});
Tab Wrapper:-
<ul class="tabs" role="tablist">
<li role="presentation">Tab 1</li>
<li role="presentation">Tab 2</li>
<li role="presentation">Tab 3</li>
</ul>
<div class="tab-panels">
<div class="panel" id="tab-1" role="tabpanel" aria-hidden="false">…</div>
<div class="panel" id="tab-2" role="tabpanel" aria-hidden="true">…</div>
<div class="panel" id="tab-3" role="tabpanel" aria-hidden="true">…</div>
</div>
One approach is to move an element higher in the DOM. The element at the top of the DOM tree will be focused first using Tabs as compared to lower ones.
For what's it's worth, I don't think you actually need to use an each loop here or even $(this).
jQuery is configured to execute functional arguments in place of plain values and runs the function once per loop, similarly to how an each loop would work.
You can read more on how that works in the official jQuery documentation here: .val( function )
So rather than using an each loop, you can accomplish the same effect using just this:
let n = 1;
$(':input:visible').attr('tabindex', function() { return n++; });
or if you're okay using the modern ES6 arrow function syntax—
let n = 1;
$(':input:visible').attr('tabindex', () => n++);
Using () => n++ instead of just n++ here allows jQuery to run the function for each instance's value rather than taking in the initial value of n++ and applying it to all matching elements.
I am using mootools and i want to load in a div (named response) content.
The div content i pass in javascript with $('response').set('html', content) where content is variable. in the content variable i have some html code with buttons and want to create a event handle ( click ).
the content I load with a json request and pass to the element:
<div id="undo">
<ul>
<li> <button value="1">foo</button> </li>
<li> <button value="2">bar</button> </li>
</ul>
</div>
my javascript looks like
$('undo').addEvents({
'click:relay(button)': function(ev, element){
alert('a button clicked!');
}
});
but I don't know why the event didn't work.
I think the problem is that $('undo') doesn't exist when the dom object is ready but i don't know how to fix this.
Delegate further up the dom tree to an element that is there at the time of domready block running. eg, if you have <div id=content>... </div> (or response if it's static)
document.id('content').addEvents({
'click:relay(#undo button)': function(event, element){
event.stop();
console.log(element.get('value'));
}
});
given that you inject your data there later on, this will work fine.