mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 08:18:59 +00:00
Document.activeElement, focus and blur
This commit is contained in:
81
src/browser/tests/document/focus.html
Normal file
81
src/browser/tests/document/focus.html
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../testing.js"></script>
|
||||||
|
<body>
|
||||||
|
<input id="input1" type="text">
|
||||||
|
<input id="input2" type="text">
|
||||||
|
<button id="btn1">Button</button>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script id="activeElement_default">
|
||||||
|
{
|
||||||
|
const body = document.querySelector('body');
|
||||||
|
body.focus();
|
||||||
|
testing.expectEqual('BODY', document.activeElement.tagName);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="focus_method">
|
||||||
|
{
|
||||||
|
const input1 = $('#input1');
|
||||||
|
input1.focus();
|
||||||
|
testing.expectEqual(input1, document.activeElement);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="focus_events">
|
||||||
|
{
|
||||||
|
const input1 = $('#input1');
|
||||||
|
const input2 = $('#input2');
|
||||||
|
|
||||||
|
// Explicitly blur any focused element
|
||||||
|
if (document.activeElement) {
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
let focusCount = 0;
|
||||||
|
let blurCount = 0;
|
||||||
|
|
||||||
|
input1.addEventListener('focus', () => focusCount++);
|
||||||
|
input1.addEventListener('blur', () => blurCount++);
|
||||||
|
input2.addEventListener('focus', () => focusCount++);
|
||||||
|
|
||||||
|
input1.focus();
|
||||||
|
testing.expectEqual(1, focusCount);
|
||||||
|
testing.expectEqual(0, blurCount);
|
||||||
|
|
||||||
|
input2.focus();
|
||||||
|
testing.expectEqual(2, focusCount);
|
||||||
|
testing.expectEqual(1, blurCount);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="blur_method">
|
||||||
|
{
|
||||||
|
const btn = $('#btn1');
|
||||||
|
btn.focus();
|
||||||
|
testing.expectEqual(btn, document.activeElement);
|
||||||
|
|
||||||
|
btn.blur();
|
||||||
|
testing.expectEqual('BODY', document.activeElement.tagName);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="focus_already_focused">
|
||||||
|
{
|
||||||
|
const input1 = $('#input1');
|
||||||
|
|
||||||
|
// Explicitly blur any focused element
|
||||||
|
if (document.activeElement) {
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
let focusCount = 0;
|
||||||
|
input1.addEventListener('focus', () => focusCount++);
|
||||||
|
|
||||||
|
input1.focus();
|
||||||
|
testing.expectEqual(1, focusCount);
|
||||||
|
|
||||||
|
input1.focus();
|
||||||
|
testing.expectEqual(1, focusCount);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -24,6 +24,7 @@ _location: ?*Location = null,
|
|||||||
_ready_state: ReadyState = .loading,
|
_ready_state: ReadyState = .loading,
|
||||||
_current_script: ?*Element.Html.Script = null,
|
_current_script: ?*Element.Html.Script = null,
|
||||||
_elements_by_id: std.StringHashMapUnmanaged(*Element) = .empty,
|
_elements_by_id: std.StringHashMapUnmanaged(*Element) = .empty,
|
||||||
|
_active_element: ?*Element = null,
|
||||||
|
|
||||||
pub const Type = union(enum) {
|
pub const Type = union(enum) {
|
||||||
generic,
|
generic,
|
||||||
@@ -155,6 +156,22 @@ pub fn getReadyState(self: *const Document) []const u8 {
|
|||||||
return @tagName(self._ready_state);
|
return @tagName(self._ready_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getActiveElement(self: *Document) ?*Element {
|
||||||
|
if (self._active_element) |el| {
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to body if it exists
|
||||||
|
if (self.is(HTMLDocument)) |html_doc| {
|
||||||
|
if (html_doc.getBody()) |body| {
|
||||||
|
return body.asElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to document element
|
||||||
|
return self.getDocumentElement();
|
||||||
|
}
|
||||||
|
|
||||||
const ReadyState = enum {
|
const ReadyState = enum {
|
||||||
loading,
|
loading,
|
||||||
interactive,
|
interactive,
|
||||||
@@ -182,6 +199,7 @@ pub const JsApi = struct {
|
|||||||
pub const documentElement = bridge.accessor(Document.getDocumentElement, null, .{});
|
pub const documentElement = bridge.accessor(Document.getDocumentElement, null, .{});
|
||||||
pub const readyState = bridge.accessor(Document.getReadyState, null, .{});
|
pub const readyState = bridge.accessor(Document.getReadyState, null, .{});
|
||||||
pub const implementation = bridge.accessor(Document.getImplementation, null, .{});
|
pub const implementation = bridge.accessor(Document.getImplementation, null, .{});
|
||||||
|
pub const activeElement = bridge.accessor(Document.getActiveElement, null, .{});
|
||||||
|
|
||||||
pub const createElement = bridge.function(Document.createElement, .{});
|
pub const createElement = bridge.function(Document.createElement, .{});
|
||||||
pub const createElementNS = bridge.function(Document.createElementNS, .{});
|
pub const createElementNS = bridge.function(Document.createElementNS, .{});
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ pub fn asNode(self: *Element) *Node {
|
|||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn asEventTarget(self: *Element) *@import("EventTarget.zig") {
|
||||||
|
return self._proto.asEventTarget();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asConstNode(self: *const Element) *const Node {
|
pub fn asConstNode(self: *const Element) *const Node {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
@@ -390,6 +394,32 @@ pub fn remove(self: *Element, page: *Page) void {
|
|||||||
page.removeNode(parent, node, .{ .will_be_reconnected = false });
|
page.removeNode(parent, node, .{ .will_be_reconnected = false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn focus(self: *Element, page: *Page) !void {
|
||||||
|
const Event = @import("Event.zig");
|
||||||
|
|
||||||
|
if (page.document._active_element) |old| {
|
||||||
|
if (old == self) return;
|
||||||
|
|
||||||
|
const blur_event = try Event.init("blur", null, page);
|
||||||
|
try page._event_manager.dispatch(old.asEventTarget(), blur_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
page.document._active_element = self;
|
||||||
|
|
||||||
|
const focus_event = try Event.init("focus", null, page);
|
||||||
|
try page._event_manager.dispatch(self.asEventTarget(), focus_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blur(self: *Element, page: *Page) !void {
|
||||||
|
if (page.document._active_element != self) return;
|
||||||
|
|
||||||
|
page.document._active_element = null;
|
||||||
|
|
||||||
|
const Event = @import("Event.zig");
|
||||||
|
const blur_event = try Event.init("blur", null, page);
|
||||||
|
try page._event_manager.dispatch(self.asEventTarget(), blur_event);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getChildren(self: *Element, page: *Page) !collections.NodeLive(.child_elements) {
|
pub fn getChildren(self: *Element, page: *Page) !collections.NodeLive(.child_elements) {
|
||||||
return collections.NodeLive(.child_elements).init(null, self.asNode(), {}, page);
|
return collections.NodeLive(.child_elements).init(null, self.asNode(), {}, page);
|
||||||
}
|
}
|
||||||
@@ -831,6 +861,8 @@ pub const JsApi = struct {
|
|||||||
pub const getElementsByTagName = bridge.function(Element.getElementsByTagName, .{});
|
pub const getElementsByTagName = bridge.function(Element.getElementsByTagName, .{});
|
||||||
pub const getElementsByClassName = bridge.function(Element.getElementsByClassName, .{});
|
pub const getElementsByClassName = bridge.function(Element.getElementsByClassName, .{});
|
||||||
pub const children = bridge.accessor(Element.getChildren, null, .{});
|
pub const children = bridge.accessor(Element.getChildren, null, .{});
|
||||||
|
pub const focus = bridge.function(Element.focus, .{});
|
||||||
|
pub const blur = bridge.function(Element.blur, .{});
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Build = struct {
|
pub const Build = struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user