Skip to content

Commit

Permalink
Merge pull request #309 from churkin/i294
Browse files Browse the repository at this point in the history
Tap doesn't set hoverPseudoClass on an element in Android (close #294)
  • Loading branch information
helen-dikareva committed Dec 2, 2015
2 parents 820c3e6 + 174dbda commit 7d4a042
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 173 deletions.
1 change: 1 addition & 0 deletions src/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class Hammerhead {

this.eventSandbox = {
listeners: this.sandbox.event.listeners,
hover: this.sandbox.event.hover,
focusBlur: this.sandbox.event.focusBlur,
elementEditingWatcher: this.sandbox.event.elementEditingWatcher,
eventSimulator: this.sandbox.event.eventSimulator,
Expand Down
69 changes: 0 additions & 69 deletions src/client/sandbox/event/focus-blur.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ export default class FocusBlurSandbox extends SandboxBase {

this.shouldDisableOuterFocusHandlers = false;
this.topWindow = null;
this.hoverElementFixed = false;
this.lastHoveredElement = null;
this.lastFocusedElement = null;

this.eventSimulator = eventSimulator;
Expand Down Expand Up @@ -48,63 +46,6 @@ export default class FocusBlurSandbox extends SandboxBase {
styleUtils.setScrollTop(el, scroll.top);
}

_onMouseOverHandler (e) {
if (this.hoverElementFixed || domUtils.isShadowUIElement(e.target))
return;

// NOTE: In this method, we go up to the tree of elements and look for a joint parent for the
// previous and new hovered elements. Processing is needed only until that parent is found.
// In this case, we’ll reduce the number of dom calls.
var clearHoverMarkerUntilJointParent = newHoveredElement => {
var jointParent = null;

if (this.lastHoveredElement) {
var el = this.lastHoveredElement;

while (el && el.tagName) {
// NOTE: Check that the current element is a joint parent for the hovered elements.
if (el.contains && !el.contains(newHoveredElement)) {
nativeMethods.removeAttribute.call(el, INTERNAL_ATTRS.hoverPseudoClass);
el = el.parentNode;
}
else
break;
}

jointParent = el;

if (jointParent)
nativeMethods.removeAttribute.call(jointParent, INTERNAL_ATTRS.hoverPseudoClass);
}

return jointParent;
};

var setHoverMarker = (newHoveredElement, jointParent) => {
if (jointParent)
nativeMethods.setAttribute.call(jointParent, INTERNAL_ATTRS.hoverPseudoClass, '');

while (newHoveredElement && newHoveredElement.tagName) {
// NOTE: Assign a pseudo-class marker to the elements until the joint parent is found.
if (newHoveredElement !== jointParent) {
nativeMethods.setAttribute.call(newHoveredElement, INTERNAL_ATTRS.hoverPseudoClass, '');
newHoveredElement = newHoveredElement.parentNode;
}
else
break;
}
};

var jointParent = clearHoverMarkerUntilJointParent(e.target);

setHoverMarker(e.target, jointParent);
}

_onMouseOut (e) {
if (!domUtils.isShadowUIElement(e.target))
this.lastHoveredElement = e.target;
}

_onChangeActiveElement (activeElement) {
if (this.lastFocusedElement === activeElement)
return;
Expand Down Expand Up @@ -242,8 +183,6 @@ export default class FocusBlurSandbox extends SandboxBase {
this.activeWindowTracker.attach(window);
this.topWindow = domUtils.isCrossDomainWindows(window, window.top) ? window : window.top;

this.listeners.addInternalEventListener(window, ['mouseover'], e => this._onMouseOverHandler(e));
this.listeners.addInternalEventListener(window, ['mouseout'], e => this._onMouseOut(e));
this.listeners.addInternalEventListener(window, ['focus', 'blur'], () => this._onChangeActiveElement(this.document.activeElement));
}

Expand Down Expand Up @@ -375,14 +314,6 @@ export default class FocusBlurSandbox extends SandboxBase {
this.shouldDisableOuterFocusHandlers = false;
}

fixHoveredElement () {
this.hoverElementFixed = true;
}

freeHoveredElement () {
this.hoverElementFixed = false;
}

blur (el, callback, withoutHandlers, isNativeBlur) {
var activeElement = domUtils.getActiveElement(domUtils.findDocument(el));
// NOTE: In IE, if you call the focus() or blur() method from script, an active element is changed
Expand Down
82 changes: 82 additions & 0 deletions src/client/sandbox/event/hover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import INTERNAL_ATTRS from '../../../processing/dom/internal-attributes';
import SandboxBase from '../base';
import nativeMethods from '../native-methods';
import * as domUtils from '../../utils/dom';

export default class HoverSandbox extends SandboxBase {
constructor (listeners) {
super();

this.listeners = listeners;

this.hoverElementFixed = false;
this.lastHoveredElement = null;
}

static _setHoverMarker (newHoveredElement, jointParent) {
if (jointParent)
nativeMethods.setAttribute.call(jointParent, INTERNAL_ATTRS.hoverPseudoClass, '');

while (newHoveredElement && newHoveredElement.tagName) {
// NOTE: Assign a pseudo-class marker to the elements until the joint parent is found.
if (newHoveredElement !== jointParent) {
nativeMethods.setAttribute.call(newHoveredElement, INTERNAL_ATTRS.hoverPseudoClass, '');
newHoveredElement = newHoveredElement.parentNode;
}
else
break;
}
}

// NOTE: In this method, we go up to the tree of elements and look for a joint parent for the
// previous and new hovered elements. Processing is needed only until that parent is found.
// In this case, we’ll reduce the number of dom calls.
_clearHoverMarkerUntilJointParent (newHoveredElement) {
var jointParent = null;

if (this.lastHoveredElement) {
var el = this.lastHoveredElement;

while (el && el.tagName) {
// NOTE: Check that the current element is a joint parent for the hovered elements.
if (el.contains && !el.contains(newHoveredElement)) {
nativeMethods.removeAttribute.call(el, INTERNAL_ATTRS.hoverPseudoClass);
el = el.parentNode;
}
else
break;
}

jointParent = el;

if (jointParent)
nativeMethods.removeAttribute.call(jointParent, INTERNAL_ATTRS.hoverPseudoClass);
}

return jointParent;
}

_hover (el) {
if (!this.hoverElementFixed && !domUtils.isShadowUIElement(el)) {
var jointParent = this._clearHoverMarkerUntilJointParent(el);

HoverSandbox._setHoverMarker(el, jointParent);

this.lastHoveredElement = el;
}
}

fixHoveredElement () {
this.hoverElementFixed = true;
}

freeHoveredElement () {
this.hoverElementFixed = false;
}

attach (window) {
super.attach(window);

this.listeners.addInternalEventListener(window, ['mouseover', 'touchstart'], e => this._hover(e.target));
}
}
3 changes: 3 additions & 0 deletions src/client/sandbox/event/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import FocusBlurSandbox from './focus-blur';
import HoverSandbox from './hover';
import Listeners from './listeners';
import Selection from './selection';
import SandboxBase from '../base';
Expand All @@ -22,6 +23,7 @@ export default class EventSandbox extends SandboxBase {
this.eventSimulator = eventSimulator;
this.focusBlur = new FocusBlurSandbox(listeners, eventSimulator, messageSandbox, shadowUI, timerSandbox, elementEditingWatcher);
this.selection = new Selection(this);
this.hover = new HoverSandbox(listeners);
this.shadowUI = shadowUI;
this.message = messageSandbox;

Expand Down Expand Up @@ -189,6 +191,7 @@ export default class EventSandbox extends SandboxBase {
this.message.attach(window);
this.timers.attach(window);
this.focusBlur.attach(window);
this.hover.attach(window);
}

overrideElement (el, overridePrototypeMeths) {
Expand Down
104 changes: 0 additions & 104 deletions test/client/fixtures/sandbox/event/event-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
var INTERNAL_ATTRS = hammerhead.get('../processing/dom/internal-attributes');

var browserUtils = hammerhead.utils.browser;
var nativeMethods = hammerhead.nativeMethods;
var iframeSandbox = hammerhead.sandbox.iframe;
Expand All @@ -17,79 +15,6 @@ QUnit.testDone(function () {
iframeSandbox.off(iframeSandbox.IFRAME_READY_TO_INIT_EVENT, initIframeTestHandler);
});

var lastHovered = null;

function hoverElement (el) {
if (lastHovered)
dispatchMouseEvent(lastHovered, 'mouseout');
dispatchMouseEvent(el, 'mouseover');
lastHovered = el;
}

function dispatchMouseEvent (el, type) {
var evt = null;
var e = {
bubbles: true,
cancelable: true,
view: window,
detail: 0,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
button: 0,
relatedTarget: void 0
};

if (document.createEvent) {
evt = document.createEvent('MouseEvents');
evt.initMouseEvent(type, e.bubbles, e.cancelable, e.view, e.detail, e.screenX, e.screenY,
e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, document.body.parentNode);
}
else if (document.createEventObject) {
evt = document.createEventObject();
for (var prop in e)
evt[prop] = e[prop];
evt.button = { 0: 1, 1: 4, 2: 2 }[evt.button] || evt.button;
}

if (el.dispatchEvent)
nativeMethods.dispatchEvent.call(el, evt);
else if (el.fireEvent)
nativeMethods.fireEvent.call(el, 'on' + type, evt);

lastHovered = el;
}

function isHovered (el) {
return el.getAttribute(INTERNAL_ATTRS.hoverPseudoClass) === '';
}

if (!browserUtils.hasTouchEvents) {
test('hover pseudo class', function () {
var $parent = $('<div style="width:100px; height:100px; background-color: Red" class="parent">').appendTo($('body'));
var $child = $('<div style="width:50px; height:50px; background-color: Blue" class="child">').appendTo($parent);

ok(!isHovered($parent[0]));
ok(!isHovered($child[0]));

hoverElement($parent[0]);
ok(isHovered($parent[0]));
ok(!isHovered($child[0]));

hoverElement($child[0]);
ok(isHovered($parent[0]));
ok(isHovered($child[0]));

hoverElement($('body')[0]);
ok(!isHovered($parent[0]));
ok(!isHovered($child[0]));

$parent.remove();
$child.remove();
});
}

if (!browserUtils.isIE9) {
asyncTest('override setTimeout error (T203986)', function () {
var str = 'success';
Expand Down Expand Up @@ -248,35 +173,6 @@ test('firing and dispatching the events created in different ways (Q532574)', fu
$div.remove();
});

if (!browserUtils.hasTouchEvents) {
test('focusBlur.fixHoveredElement, focusBlur.freeHoveredElement (B254111)', function () {
var $parent = $('<div style="width:100px; height:100px; background-color: Red" class="parent">').appendTo($('body'));
var $child = $('<div style="width:50px; height:50px; background-color: Blue" class="child">').appendTo($parent);

ok(!isHovered($parent[0]));
ok(!isHovered($child[0]));

focusBlur.fixHoveredElement();

hoverElement($parent[0]);
ok(!isHovered($parent[0]));
ok(!isHovered($child[0]));

focusBlur.freeHoveredElement();

hoverElement($child[0]);
ok(isHovered($parent[0]));
ok(isHovered($child[0]));

hoverElement($('body')[0]);
ok(!isHovered($parent[0]));
ok(!isHovered($child[0]));

$parent.remove();
$child.remove();
});
}

test('attachEvent, fireEvent, detachEvent must be overriden (T239606)', function () {
var el = nativeMethods.createElement.call(document, 'A');

Expand Down
66 changes: 66 additions & 0 deletions test/client/fixtures/sandbox/event/hover-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
var INTERNAL_ATTRS = hammerhead.get('../processing/dom/internal-attributes');

var hover = hammerhead.sandbox.event.hover;
var browserUtils = hammerhead.utils.browser;
var eventSimulator = hammerhead.sandbox.event.eventSimulator;

function hoverElement (el) {
if (browserUtils.hasTouchEvents)
eventSimulator.touchstart(el, {});
else
eventSimulator.mouseover(el, {});
}

function isHovered (el) {
return el.getAttribute(INTERNAL_ATTRS.hoverPseudoClass) === '';
}

test('hover pseudo class', function () {
var $parent = $('<div style="width:100px; height:100px; background-color: Red" class="parent">').appendTo($('body'));
var $child = $('<div style="width:50px; height:50px; background-color: Blue" class="child">').appendTo($parent);

ok(!isHovered($parent[0]));
ok(!isHovered($child[0]));

hoverElement($parent[0]);
ok(isHovered($parent[0]));
ok(!isHovered($child[0]));

hoverElement($child[0]);
ok(isHovered($parent[0]));
ok(isHovered($child[0]));

hoverElement($('body')[0]);
ok(!isHovered($parent[0]));
ok(!isHovered($child[0]));

$parent.remove();
$child.remove();
});

test('hover.fixHoveredElement, hover.freeHoveredElement (B254111)', function () {
var $parent = $('<div style="width:100px; height:100px; background-color: Red" class="parent">').appendTo($('body'));
var $child = $('<div style="width:50px; height:50px; background-color: Blue" class="child">').appendTo($parent);

ok(!isHovered($parent[0]));
ok(!isHovered($child[0]));

hover.fixHoveredElement();

hoverElement($parent[0]);
ok(!isHovered($parent[0]));
ok(!isHovered($child[0]));

hover.freeHoveredElement();

hoverElement($child[0]);
ok(isHovered($parent[0]));
ok(isHovered($child[0]));

hoverElement($('body')[0]);
ok(!isHovered($parent[0]));
ok(!isHovered($child[0]));

$parent.remove();
$child.remove();
});

0 comments on commit 7d4a042

Please sign in to comment.