Hammerjs events order for nested elements - nested

Consider two nested divs with "click" event handlers:
var parent = document.getElementById("parent");
parent.addEventListener("click", function() {
console.log("parent click");
});
var child = document.getElementById("child");
child.addEventListener("click", function() {
console.log("child click");
});
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="parent" style="width:150px;height:100px;border: 1px solid black">
<div id="child" style="width:75px;height:50px;border: 1px solid black"></div>
</div>
</body>
</html>
When one clicks on the nested element the "click" event "bubbles", so the output in the console looks like this:
child click
parent click
Now consider similar example with Hammerjs involved:
var parent = document.getElementById("parent");
var hammer1 = new Hammer(parent).on("tap", function() {
console.log("parent click");
});
var child = document.getElementById("child");
var hammer2 = new Hammer(child).on("tap", function() {
console.log("child click");
});
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.js"></script>
</head>
<body>
<div id="parent" style="width:150px;height:100px;border: 1px solid black">
<div id="child" style="width:75px;height:50px;border: 1px solid black">
</div>
</div>
</body>
</html>
When one clicks on the nested element the console output is the opposite (parent's event handled first):
parent click
child click
To get the "bubble" event order one has to register child event handler before the parent's one.
Is there any way to achieve the same effect without messing with the order of event handler registrations?

Using jQuery, here's one trick I used successfully:
Normally you would do something like this:
$("some_selector").hammer().on('press', handlePress);
But to get the order right with Hammer events, this works:
$($("some_selector").get().reverse()).hammer().on('press', handlePress);
It's just a trick to get jQuery to assign the events in the reverse order (from the leaf children up the tree to the parents and parents parents etc.)

I looked into this because I ran into the very same issue.
To understand the problem you need to know how hammerjs recognizes gestures (consisting of several events); in this case the "tap"-gesture.
Hammer installs so-called recognizers that get activated when certain events travel (bubble) along the DOM.
The "tap"-event gets triggered when the "pointerup"-event bubbles up to the window.
When the "pointerup"-event reaches the window (event.currentTarget is window) all registered recognizers get actived on that node's event and that is in the order of installation.
To respect the propagation line the "tap"-recognizer would have to fire on their respective element's "pointerup" event rather than on window.
Hope that is a somewhat consistent explanation.

Related

Automating click to image-mapped actions/links using Node/Nightwatch

How does one use NightwatchJs to automate clicking a specific part of an image? My naive approach is to select the coords attribute that matches the specific area of the image I'd like to trigger; but it doesn't work.
<img src="..." usemap="#example">
<map name="example" id="example">
<area shape="rect" coords="336,10,401,32" href="...">
<area shape="rect" coords="25,171,97,198" href="...">
...
</map>
Anyone encounter this issue or know of a work around? Thanks!
If I were you, I would play with the position of area elements inside the map element using CSS selectors like :first-child or :first-of-type. Here is a minimal working example:
PNG (map.png)
HTML/JS (index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Nightwatch</title>
</head>
<body>
<img src="map.png" usemap="#map">
<map name="map">
<area shape="circle" coords="51,51,29">
</map>
<script>
// When the red area is clicked, we should display an alert.
var area = document.querySelector('area');
area.addEventListener('click', function () {
alert('OK');
}, false);
</script>
</body>
</html>
Nightwatch (script.js)
module.exports = {
'Clickable image map': function (browser) {
browser
.url('http://localhost:8000/index.html')
.waitForElementPresent('map', 1000)
.click('map > area:first-child');
// ...
},
};
Command
If your environment is properly set up, you can run the script with nightwatch -t tests/script.js. You will see the alert, meaning that the red area has been clicked by Nightwatch.

Google Translate widget - responsive

On my Web page I put translate widget when i resize browsers widged does not change size
I tried change css but i can change only css for Iframe
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<script type="text/javascript" src="script.js"></script>
<script type="text/javascript">
function googleTranslateElementInit() {
new google.translate.TranslateElement({
pageLanguage: 'en',
layout: google.translate.TranslateElement.InlineLayout.SIMPLE
}, 'google_translate_element');
}
</script>
<script type="text/javascript"
src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
</head>
<body>
<div id="google_translate_element"></div>
</body>
</html>
do you heve any solution?
Google Translate popup Layout - responsive fixed
<div id="google_translate_element" style="text-align: center;"></div>
<style>
.goog-te-banner-frame.skiptranslate {
display: none !important;
}
body {
top: 0px !important;
}
.goog-te-menu-frame {
max-width:100% !important;
}
.goog-te-menu2 {
max-width: 100% !important;
overflow-x: scroll !important;
box-sizing:border-box !important;
height:auto !important;
}
</style>
<script type="text/javascript">
function googleTranslateElementInit() {
new google.translate.TranslateElement({
pageLanguage: 'en',
autoDisplay: false,
layout: google.translate.TranslateElement.InlineLayout.SIMPLE
}, 'google_translate_element');
function changeGoogleStyles() {
if($('.goog-te-menu-frame').contents().find('.goog-te-menu2').length) {
$('.goog-te-menu-frame').contents().find('.goog-te-menu2').css({
'max-width':'100%',
'overflow-x':'auto',
'box-sizing':'border-box',
'height':'auto'
});
} else {
setTimeout(changeGoogleStyles, 50);
}
}
changeGoogleStyles();
}
</script>
<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
Not the solution to resizing issue but maybe helpful. You can change the default layout in the init function of the google translate selector.
Change in the line
layout: google.translate.TranslateElement.InlineLayout.SIMPLE to layout: google.translate.TranslateElement.InlineLayout.VERTICAL or layout: google.translate.TranslateElement.InlineLayout.HORIZONTAL.
These options will show the language choices in a vertical dropdown with also either the 'Made possible by Google Translate' label under or next to it.
You will not be able to adjust the layout of this widget using strictly CSS. The <a> elements containing links for all of the languages to choose from are laid out in <td> cells in rows. Therefore, they will not be laid out dynamically with resizing.
You can however, get around this by getting all the language links in the contained <iframe> and appending them to a <div> outside the <table>.
This should perform what you seek though may still require much CSS tweaking. Much of Google's UI elements are laid out manually with pixel dimensions and overridden attributes like overflow:hidden to avoid default (sometimes inconsistent) browser behavior. This solution may require a fair bit of [poking around the DOM][1] to determine where these adjustments are being done.
This should be executed in the top-most frame to access the <iframe> element and make changes to its CSS. Note that the selector is not a unique ID so it may return a different <iframe> than expected depending on the contents of your page.
var iframe = document.querySelector('.goog-te-menu-frame.skiptranslate');
if (iframe === null) {
console.error('Could not find iframe of language links');
} else {
// Force <iframe> visibility and auto-resizing
iframe.style.display = '';
iframe.style.height = '';
iframe.style.width = '99%!important';
This should be executed in the about:blank frame of the <iframe> to have access to the elements within.
// Get all the <a> elements
var anchors = document.querySelectorAll('a.goog-te-menu2-item');
anchors = Array.prototype.slice.call(language_anchors);
if (anchors.length < 1) {
console.error('Found no language links');
}
// Get the conatiner <div> that holds the table of links
var div = document.getElementById(':1.menuBody');
if (div === null) {
console.error('Could not find div containing table of language links');
} else {
// Remove width/height attributes to have <div> resize
div.style.height = '';
div.style.width = '';
// Iterate through all language links
anchors.forEach(function (a) {
// Set display to inline=block so its rendered like text
// This is what gets the elements onto a new line if they don't fit
a.style.display = 'inline-block';
// Append them directly to the <div>
div.appendChild(a);
});
// Remove the now empty <table> to keep things clean
div.removeChild(div.querySelector('table'));
}
This may break easily if Google changes their CSS class names or element IDs. Keep that in mind and happy rendering.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<script type="text/javascript" src="script.js"></script>
<script type="text/javascript">
function googleTranslateElementInit() {
new google.translate.TranslateElement({
pageLanguage: 'en',
layout: google.translate.TranslateElement.InlineLayout.HORIZONTAL
}, 'google_translate_element');
}
</script>
<script type="text/javascript"
src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
</head>
<body>
<div id="google_translate_element"></div>
</body>
</html>
YOU NEED TO CHANGE THE "SIMPLE" TO "HORIZONTAL"
You can put this in your css file for the theme that you're using. Tweak it to make it work for you. Hope that helps!
select.goog-te-combo{width:100%!important;}

Change polymer attributes externally

I see a lot of questions regarding on how to listen for changes in attributes. But none on how to actually change them.
Even in debug, I can't find the attributes in the object tree. How do I achieve this? is there a more polymeric way of doing the following ?
<polymer-element name="my-element" attributes="owner">
<template>
<p id="el">{{owner}}</p>
</template>
<script>
Polymer({
owner: "Miguel",
});
</script>
</polymer-element>
<my-element id="el1" owner="blabla"></my-element>
<script>
$(document).ready(function(){
$("el1").owner = "Mary"
})
</script>
It prints blablab, but doesn't change it to Mary
In 0.5, you need to wait for polymer-ready. See docs here.
<head>
<link rel="import" href="path/to/x-foo.html">
</head>
<body>
<x-foo></x-foo>
<script>
window.addEventListener('polymer-ready', function(e) {
var xFoo = document.querySelector('x-foo');
xFoo.barProperty = 'baz';
});
</script>
</body>
Ideally, your app would be one element like <my-app></my-app> so that you'd have a binding for the owner attribute like so:
<my-element owner="{{owner}}"></my-element>
And my-element would reside in another Polymer element in which you can set the owner attribute like so:
// Parent Polymer element
Polymer({
_someFunctionYouCall: function() {
this.owner = 'Mary';
}
});

Create HTML element with YUI

I am using following code to create a html element in the page body with using YUI.
This code doesn't produce any error.
The issue is, the paragraph element is not created in the html page.
<html>
<head>
<title>YUI Test</title>
<meta charset="UTF-8">
<script src="http://yui.yahooapis.com/3.14.1/build/yui/yui-min.js"></script>
<script>
// Create a YUI sandbox on your page.
YUI().use('node', function(Y) {
// Create DOM nodes.
var contentNode = Y.Node.create('<p>');
contentNode.setHTML('This is a para created by YUI...');
});
</script>
</head>
<body>
<h1>Page body section...</h1>
</body>
</html>
The node is created, but it is also detached from the DOM. You have to attach it to the DOM by using either
Y.one('body').append(contentNode);
or
contentNode.appendTo(Y.one('body'));
or
Y.one('nav.main-navigation').insert(contentNode, 'before');
or any of the other methods for manipulating dom in YUI.

SVG shapes added to Raphael group fire events as if separate

Imagine this:
2 concentric circles, with the smaller one over the larger one so
that both are visible
both are added to a Raphael group (set)
the group has mouseout and mouseover event handlers
Problem:
When the cursor goes from one circle to the other, both event handlers fire, as if they were added separately to each circle.
What I want is for events to be handled for the entire group as if it was a single shape.
How can I achieve that?
Here's the html code:
<!DOCTYPE html>
<html>
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.0.0/raphael-min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
<div id="target"></div>
</body>
</html>
Javascript:
var W=200;
var H=200;
var paper=new Raphael(document.getElementById('target'),W,H);
var c1=paper.circle(W/2,H/2,70).attr({fill:'orange','stroke-width':4,stroke:'red',opacity:0.7});
var c2=paper.circle(W/2,H/2,50).attr({fill:'green','stroke-width':4,stroke:'yellow',opacity:0.7});
var group=paper.set();
group.push(c1,c2);
var count=0;
group.mouseover(function()
{
console.log('IN',++count);
});
group.mouseout(function()
{
console.log('OUT',++count);
});
and the CSS code:
#target{width:200px;}
Run the above code and see the results here: http://jsbin.com/ivules/7.
Console shows IN and OUT logs.
Just move the mouse between the two circles' bounds.
For your mouseout() function, please try:
group.mouseout(function()
{
this.mouseout(function(){
console.log('OUT',++count);
});
});
When you hover over the outer circle, you will get "IN". When you hover over the inner circle, you will get "IN" again. When you leave the circle entirely, you will finally get "OUT".
If that's too many "IN"'s, try creating an invisible circle, place it over top the current 2 circles, and only add the mouseover event to that circle. For instance, try:
c3=paper.circle(W/2,H/2,70).attr({fill:'orange','stroke-width':4,stroke:'red',opacity:0});
c3.mouseover(function()
{
console.log('IN',++count);
});
c3.mouseout(function()
{
console.log('OUT',++count);
});

Resources