Polymer - can not click event outside of element to close itself - polymer

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.

Related

Polymer event on-content-scroll not firing

I have an iron-list which I add new items to while scrolling to the bottom using iron-scroll-threshold. That works fine.
What I also need is a general event which is fired when scrolling stops.
I need that to know whether the listed items (m.message) have been seen by the user, by checking which items are currently visible in the view-port after scrolling, and then marking them as "read".
<div class="container" on-content-scroll="_scrollHandler">
<iron-scroll-threshold id="threshold" scroll-target="mlist" lower-threshold="500" on-lower-threshold="_loadMoreData"></iron-scroll-threshold>
<iron-list items="[[messages]]" id="mlist" as="m">
<template>
<div>
<p>[[m.message]]</p>
</div>
</template>
</iron-list>
</div>
The handler _scrollHandler is never fired however.
What would be necessary to get an event after scrolling ends?
You need the style: overflow: auto on the div.container. This will make sure the scroll event will invoke.
I could not find any such event as content-scroll, but with the changes above you should be able to change your HTML to bind against the handler like: on-scroll="_scrollHandler".
To detect if scrolling has stopped, I'd recommend using Polymer.debounce to have the callback set the isScrolling state to false like:
app._scrollHandler = function (e) {
app.isScrolling = true
app.debounce('scroll-handler', _ => {
app.isScrolling = false
}, 20)
}
It works at the end by moving on-scroll="_scrollHandler" to the iron-list:
<div class="container">
<iron-scroll-threshold id="threshold" scroll-target="mlist" lower-threshold="500" on-lower-threshold="_loadMoreData"></iron-scroll-threshold>
<iron-list items="[[messages]]" id="mlist" as="m" on-scroll="_scrollHandler">
<template>
<div>
<p>[[m.message]]</p>
</div>
</template>
</iron-list>
</div>
With the function being:
_scrollHandler: function() {
this.debounce("markAsRead", function(e) {
console.log("debounce");
}, 500);
}
Edit:
In case the iron-scroll-threshold wraps the iron-list, you need to move on-scroll to the iron-scroll-threshold-element:
<iron-scroll-threshold on-scroll="_scrollHandler" id="threshold" on-lower-threshold="_loadMore">
<iron-list scroll-target="threshold">...</iron-list>
</iron-scroll-threshold>

How to attach a Click handler to a DOM element

How do I get a DOM element and attach an event onClick?. I tried this code but it does not work:
<div>
<h1 id="some_id">
click here
</h1>
</div>
JavaScript:
Ext.onReady(function() {
if(Ext.getDom('some_id'))
{
var elDom = Ext.getDom('some_id');
elDom.on('click', function(){
Ext.Msg.alert('Status', 'Already get the element from the dom');
});
}
});
Fiddle: https://fiddle.sencha.com/#fiddle/2fl
Another way to do this is to get the Ext.dom.Element instead of the actual DOM node. This will allow you to use .on(). This is done by using Ext.get() instead of Ext.getDom().
var elDom = Ext.get('some_id');
if(elDom) {
elDom.on('click', function() {
Ext.Msg.alert('Status', 'Already get the element from the dom');
});
}

How do you produce a dialog box on hover over checkbox via jQuery

Since I don't know much about jQuery I have am not being able to produce a dialog box on hover over a checkbox. Any suggestion would be helpful. Below is my code:
<input type="checkbox" id="employee-id" name="employeeId" onmouseover="produceDialog()">
<div id="employee-info-div"></div>
Similarly my jQuery is:
produceDialog(){
$("employee-info-div").dialog({
open : function ( event, ui ) {
$(".ui-dialog-titlebar-close").hide();
},
dialogClass : 'fixed-dialog',
resizable : false,
height : 150,
width : 250,
modal : false,
create : function ( event ) {
$(event.target).parent().css('position', 'fixed');
},
});
}
This may be the example you are looking for:
Working jsFiddle here
Below is a stand-alone example, which should just be copy/play.
Notes:
The element $('#employee-info-div'); was assigned to a variable to make code more efficient and faster to type. (More efficient b/c only check DOM once for the element, retrieve from variable after that.)
Used jQuery hover() method to open the dialog, but initialized the dialog separately (upon document ready). Note that the hover method must have two functions associated with it; the second function need not do anything but it must be there.
The hover-IN method assigned to the class $('.employee-id') runs the code $('#employee-info-div').dialog('open');, which opens the dialog. Note that the 2nd element is accessed via variable name.
Copy/Paste the following code into a separate document in your webroot and run, OR just use the above jsFiddle link to see it all in action.
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/jquery-ui.min.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.9.1/themes/base/jquery-ui.css" />
<style>
#employee-info-div{
width:40%;
float:right;
padding:5px;
background:wheat;
color:blue;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
var eid = $('#employee-info-div');
var blurb = '<h2>Employee Information:</h2>Here is some example information about this employee. It can be text inserted like this, or it can be information retrieved from a database via AJAX. For simple AJAX examples, <a target="_blank" href="http://stackoverflow.com/questions/17973386/ajax-request-callback-using-jquery/17974843#17974843"> see this StackOverflow post </a> (remember to upvote any posts that are helpful to you, please.)';
function hovIn() {
$(this).css({'font-weight':'bold','color':'blue'});
eid.html(blurb);
eid.dialog('open');
}
function hovOut() {
//eid.html(''); //<-- Causes dlg text to appear/disappear as you move mouse on/off checkbox and label
$(this).css({'font-weight':'normal','color':'black'});
}
$('.employee-id').hover(hovIn, hovOut);
eid.dialog({
autoOpen:false,
title:"Your jQueryUI Dialog",
show: "fade",
hide: "fade",
width:500, //orig defaults: width: 300, height: auto
buttons: {
Ok: function() {
$(this).dialog('close');
}
}
}); //END eid.dialog
}); //END $(document).ready()
</script>
</head>
<body>
Hover over below checkbox to see hidden DIV:<br><br>
<input type="checkbox" id="employee-id" class="employee-id" name="employeeId" ><span class="employee-id">Hover over this checkbox</span>
<div id="employee-info-div"></div>
</body>
</html>
You can bind the hover event to your checkbox:
$("#employee-id").hover(function(){
// call your produceDialog function here
});

Hover not working on AJAX

I have the below content that loads on through AJAX.
<div class="grid">
<div class="thumb">
<img alt="AlbumIcon" src="some-image.jpg">
<div style="bottom:-75px;" class="meta">
<p class="title">Title</p>
<p class="genre"> <i class="icon-film icon-white"></i>
Genre
</p>
</div>
</div>
</div>
Additionally, I have writen the following script in jquery that applies to the above 'div.grid'.
jQuery(function ($) {
$(document).ready(function () {
$(".grid").on({
mouseenter : function () {
$(this).find('.meta').stop().animate({
bottom:'0px'
},200);
},
mouseleave : function () {
$(this).find('.meta').stop().animate({
bottom:'-75px'
},200);
}
});
});
});
The script works fine when the page loads the first time. However, the hover effect doesn't work once the above div is generated via AJAX after clicking on an 'a' tag. I can't seem to figure out what's wrong here? New to all this. Can anyone help?
To append these event handlers to dynamically generated elements, you need to bind to the document or another static parent element and then specify .grid as the second argument passed to .on.
The second argument is used as a filter to determine the selected elements that trigger the event. So when the event is fired it will propagate to the document or parent element selected by jquery. The event target will then be scrutinized using the selector provided as the second argument. If the target matches the second argument, (.grid in our case), the event is fired.
You can read more in the jQuery documentation.
Also, since your using document.ready there is no need for the short hand ready statement, jquery(function($).
$(document).ready(function () {
$(document).on({
mouseenter : function () {
$(this).find('.meta').stop().animate({
bottom:'0px'
},200);
},
mouseleave : function () {
$(this).find('.meta').stop().animate({
bottom:'-75px'
},200);
}
}, ".grid");
});
you lost your binding because of ajax that overwrite your div with class=".grid"
use parent element for binding
$('.ParentElementClass').on("mouseleave", ".grid", function(){...})
more from jquery api
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. This element could be the container element of a view in a Model-View-Controller design, for example, or document if the event handler wants to monitor all bubbling events in the document. The document element is available in the head of the document before loading any other HTML, so it is safe to attach events there without waiting for the document to be ready.
Not sure what you're shooting for here but a little malformed HTML may have done it...
jsFiddle Demo
<div class="grid">
<div class="thumb">
<img alt="AlbumIcon" src="some-image.jpg" />
<div style="bottom:-75px;" class="meta">
<p class="title">Title</p>
<p class="genre"><i class="icon-film icon-white"></i>Genre</p>
</div>
</div>
</div>
$(function () {
$(".grid").on({
mouseenter: function () {
alert('entered');
$(this).find('.meta').stop().animate({
bottom: '0px'
}, 200);
},
mouseleave: function () {
alert('left');
$(this).find('.meta').stop().animate({
bottom: '-75px'
}, 200);
}
}, ".thumb");
});
});
Be sure to close img tags. They're notorious for causing intermittent glitches.
You can just use the hover function:
jQuery(function ($) {
$(document).ready(function () {
$(".grid").hover(function () { /*mouseenter*/
$(this).find('.meta').stop().animate({
bottom:'0px'
},200);
},function(){ /*mouseleave*/
$(this).find('.meta').stop().animate({
bottom:'-75px'
},200);
}
});
});
Explanation:
The first parameter function does the work of mouseenter and the second does the work of mouseleave.
I'd recommend using those both, mouseenter and mouseleave in situation when you don't want an effect back when the user gets off his mouse from the element.

destroy directive/child scope on scope destroy

I have a directive that compiles another directive and attaches it to the body with the same scope passed. This will not be the same location as the "parent" directive.
When the parent directive gets destroyed is there some way to have the child directive and scope destroy as well? I ask because after inspecting the DOM the child directive is still there.
Currently I hook into the parents $destroy event but was curious if it could be handled automatically.
jsFiddle: http://jsfiddle.net/FPx4G/1/
The child stays there as you toggle the parent, but i'd like to to be destroyed. What would be the best method to do that?
html:
<div ng-app="app">
<div ng-controller="ParentCtrl">
<button data-ng-click="toggleParent()">Toggle Parent</button>
<div data-ng-switch data-on="displayDirective">
<div data-ng-switch-when="true">
<div class="parentDirective">Parent Directive</div>
</div>
</div>
</div>
</div>
javascript:
angular.module('app', [])
.directive("parentDirective", function($compile){
return {
restrict: 'C',
link: function(scope, element){
var secondDirective = angular.element(document.createElement("div"));
secondDirective.addClass("childDirective");
document.body.appendChild(secondDirective[0]);
$compile(secondDirective)(scope);
}
}
})
.directive("childDirective", function(){
return {
restrict: 'C',
template: '<div>Child Directive</div>',
link: function(scope, element){
scope.$on("destroy", function(){
alert(1);
});
}
}
});
function ParentCtrl($scope){
$scope.displayDirective = true;
$scope.toggleParent = function(){
$scope.displayDirective = !$scope.displayDirective;
}
}
Normally, I'd just have the sub element within the original directive's template so that it's positioned correctly. The issue really comes down to dealing with z-index. The parent element is in a container that can be scrolled, so the child (in one case a custom dropdown) would be hidden/cut off if it was larger then the container. To combat this I instead create the actual child in the document body and position it relative to the parent. It would also listen in on scroll events to reposition itself. I have that all working and is just fine. It's what happens when I need to delete the parent... the child is still there.
directive("childDirective", function(){
return {
restrict: 'C',
template: '<div >Child Directive</div>',
link: function(scope, element){
scope.$on("$destroy",function() {
element.remove();
});
}
}
});
updated fiddle : http://jsfiddle.net/C8hs6/