mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 13:38:11 +00:00
LibWeb: Add textarea placeholder
This commit is contained in:
parent
091faf1aae
commit
52397d01bd
6 changed files with 85 additions and 33 deletions
|
@ -1,5 +1,8 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<textarea>
|
<textarea cols="80" rows="5">
|
||||||
This is some text
|
This is some text
|
||||||
inside a <textarea>
|
inside a <textarea>
|
||||||
</textarea>
|
</textarea>
|
||||||
|
<textarea placeholder="Textarea placeholder!" cols="80" rows="5"></textarea>
|
||||||
|
<textarea placeholder="Textarea placeholder!
|
||||||
|
That is multiline!" cols="80" rows="5"></textarea>
|
||||||
|
|
|
@ -1,38 +1,38 @@
|
||||||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||||
BlockContainer <html> at (0,0) content-size 800x56.9375 [BFC] children: not-inline
|
BlockContainer <html> at (0,0) content-size 800x65.6875 [BFC] children: not-inline
|
||||||
BlockContainer <body> at (8,8) content-size 784x40.9375 children: inline
|
BlockContainer <body> at (8,8) content-size 784x49.6875 children: inline
|
||||||
line 0 width: 403.75, height: 40.9375, bottom: 40.9375, baseline: 13.53125
|
line 0 width: 502, height: 49.6875, bottom: 49.6875, baseline: 16.921875
|
||||||
frag 0 from BlockContainer start: 0, length: 0, rect: [11,11 191.875x34.9375]
|
frag 0 from BlockContainer start: 0, length: 0, rect: [11,11 240x43.6875]
|
||||||
frag 1 from TextNode start: 0, length: 1, rect: [206,8 8x17.46875]
|
frag 1 from TextNode start: 0, length: 1, rect: [254,8 10x21.84375]
|
||||||
" "
|
" "
|
||||||
frag 2 from BlockContainer start: 0, length: 0, rect: [217,11 191.875x34.9375]
|
frag 2 from BlockContainer start: 0, length: 0, rect: [267,11 240x43.6875]
|
||||||
TextNode <#text>
|
TextNode <#text>
|
||||||
BlockContainer <textarea> at (11,11) content-size 191.875x34.9375 inline-block [BFC] children: not-inline
|
BlockContainer <textarea> at (11,11) content-size 240x43.6875 inline-block [BFC] children: not-inline
|
||||||
BlockContainer <div> at (11,11) content-size 191.875x17.46875 children: not-inline
|
BlockContainer <div> at (11,11) content-size 240x21.84375 children: not-inline
|
||||||
BlockContainer <div> at (11,11) content-size 191.875x17.46875 children: inline
|
BlockContainer <div> at (11,11) content-size 240x21.84375 children: inline
|
||||||
line 0 width: 152.203125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
line 0 width: 190.265625, height: 21.84375, bottom: 21.84375, baseline: 16.921875
|
||||||
frag 0 from TextNode start: 0, length: 17, rect: [11,11 152.203125x17.46875]
|
frag 0 from TextNode start: 0, length: 17, rect: [11,11 190.265625x21.84375]
|
||||||
"Bonjour mon amis!"
|
"Bonjour mon amis!"
|
||||||
TextNode <#text>
|
TextNode <#text>
|
||||||
TextNode <#text>
|
TextNode <#text>
|
||||||
BlockContainer <textarea> at (217,11) content-size 191.875x34.9375 inline-block [BFC] children: not-inline
|
BlockContainer <textarea> at (267,11) content-size 240x43.6875 inline-block [BFC] children: not-inline
|
||||||
BlockContainer <div> at (217,11) content-size 191.875x17.46875 children: not-inline
|
BlockContainer <div> at (267,11) content-size 240x21.84375 children: not-inline
|
||||||
BlockContainer <div> at (217,11) content-size 191.875x17.46875 children: inline
|
BlockContainer <div> at (267,11) content-size 240x21.84375 children: inline
|
||||||
line 0 width: 142.140625, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
line 0 width: 177.6875, height: 21.84375, bottom: 21.84375, baseline: 16.921875
|
||||||
frag 0 from TextNode start: 0, length: 19, rect: [217,11 142.140625x17.46875]
|
frag 0 from TextNode start: 0, length: 19, rect: [267,11 177.6875x21.84375]
|
||||||
"Well hello friends!"
|
"Well hello friends!"
|
||||||
TextNode <#text>
|
TextNode <#text>
|
||||||
TextNode <#text>
|
TextNode <#text>
|
||||||
|
|
||||||
ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
||||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x56.9375]
|
PaintableWithLines (BlockContainer<HTML>) [0,0 800x65.6875]
|
||||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x40.9375]
|
PaintableWithLines (BlockContainer<BODY>) [8,8 784x49.6875]
|
||||||
PaintableWithLines (BlockContainer<TEXTAREA>) [8,8 197.875x40.9375]
|
PaintableWithLines (BlockContainer<TEXTAREA>) [8,8 246x49.6875]
|
||||||
PaintableWithLines (BlockContainer<DIV>) [11,11 191.875x17.46875]
|
PaintableWithLines (BlockContainer<DIV>) [11,11 240x21.84375]
|
||||||
PaintableWithLines (BlockContainer<DIV>) [11,11 191.875x17.46875]
|
PaintableWithLines (BlockContainer<DIV>) [11,11 240x21.84375]
|
||||||
TextPaintable (TextNode<#text>)
|
TextPaintable (TextNode<#text>)
|
||||||
TextPaintable (TextNode<#text>)
|
TextPaintable (TextNode<#text>)
|
||||||
PaintableWithLines (BlockContainer<TEXTAREA>) [214,8 197.875x40.9375]
|
PaintableWithLines (BlockContainer<TEXTAREA>) [264,8 246x49.6875]
|
||||||
PaintableWithLines (BlockContainer<DIV>) [217,11 191.875x17.46875]
|
PaintableWithLines (BlockContainer<DIV>) [267,11 240x21.84375]
|
||||||
PaintableWithLines (BlockContainer<DIV>) [217,11 191.875x17.46875]
|
PaintableWithLines (BlockContainer<DIV>) [267,11 240x21.84375]
|
||||||
TextPaintable (TextNode<#text>)
|
TextPaintable (TextNode<#text>)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html><html><head><style>
|
||||||
<body>
|
* {
|
||||||
|
font: 20px 'SerenitySans';
|
||||||
|
}
|
||||||
|
</style></head><body>
|
||||||
<textarea>Bonjour mon amis!</textarea>
|
<textarea>Bonjour mon amis!</textarea>
|
||||||
<script>
|
<script>
|
||||||
const textarea = document.createElement("textarea");
|
const textarea = document.createElement("textarea");
|
||||||
textarea.innerText = "Well hello friends!";
|
textarea.innerText = "Well hello friends!";
|
||||||
document.body.appendChild(textarea);
|
document.body.appendChild(textarea);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ textarea {
|
||||||
height: attr(rows lh, 2lh);
|
height: attr(rows lh, 2lh);
|
||||||
}
|
}
|
||||||
|
|
||||||
input::placeholder {
|
input::placeholder, textarea::placeholder {
|
||||||
color: GrayText;
|
color: GrayText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,8 @@ void HTMLTextAreaElement::initialize(JS::Realm& realm)
|
||||||
void HTMLTextAreaElement::visit_edges(Cell::Visitor& visitor)
|
void HTMLTextAreaElement::visit_edges(Cell::Visitor& visitor)
|
||||||
{
|
{
|
||||||
Base::visit_edges(visitor);
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(m_placeholder_element);
|
||||||
|
visitor.visit(m_placeholder_text_node);
|
||||||
visitor.visit(m_inner_text_element);
|
visitor.visit(m_inner_text_element);
|
||||||
visitor.visit(m_text_node);
|
visitor.visit(m_text_node);
|
||||||
}
|
}
|
||||||
|
@ -86,6 +88,8 @@ void HTMLTextAreaElement::reset_algorithm()
|
||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
// and set the raw value of element to its child text content.
|
// and set the raw value of element to its child text content.
|
||||||
m_raw_value = child_text_content();
|
m_raw_value = child_text_content();
|
||||||
|
|
||||||
|
update_placeholder_visibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLTextAreaElement::form_associated_element_was_inserted()
|
void HTMLTextAreaElement::form_associated_element_was_inserted()
|
||||||
|
@ -181,9 +185,22 @@ void HTMLTextAreaElement::create_shadow_tree_if_needed()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm(), document(), *this, Bindings::ShadowRootMode::Closed);
|
auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm(), document(), *this, Bindings::ShadowRootMode::Closed);
|
||||||
|
set_shadow_root(shadow_root);
|
||||||
|
|
||||||
auto element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
|
auto element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
|
||||||
|
MUST(shadow_root->append_child(element));
|
||||||
|
|
||||||
|
m_placeholder_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
|
||||||
|
m_placeholder_element->set_use_pseudo_element(CSS::Selector::PseudoElement::Type::Placeholder);
|
||||||
|
MUST(element->append_child(*m_placeholder_element));
|
||||||
|
|
||||||
|
m_placeholder_text_node = heap().allocate<DOM::Text>(realm(), document(), String {});
|
||||||
|
m_placeholder_text_node->set_data(get_attribute(HTML::AttributeNames::placeholder).value_or(String {}));
|
||||||
|
m_placeholder_text_node->set_editable_text_node_owner(Badge<HTMLTextAreaElement> {}, *this);
|
||||||
|
MUST(m_placeholder_element->append_child(*m_placeholder_text_node));
|
||||||
|
|
||||||
m_inner_text_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
|
m_inner_text_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
|
||||||
|
MUST(element->append_child(*m_inner_text_element));
|
||||||
|
|
||||||
m_text_node = heap().allocate<DOM::Text>(realm(), document(), String {});
|
m_text_node = heap().allocate<DOM::Text>(realm(), document(), String {});
|
||||||
m_text_node->set_always_editable(true);
|
m_text_node->set_always_editable(true);
|
||||||
|
@ -191,11 +208,23 @@ void HTMLTextAreaElement::create_shadow_tree_if_needed()
|
||||||
// NOTE: If `children_changed()` was called before now, `m_raw_value` will hold the text content.
|
// NOTE: If `children_changed()` was called before now, `m_raw_value` will hold the text content.
|
||||||
// Otherwise, it will get filled in whenever that does get called.
|
// Otherwise, it will get filled in whenever that does get called.
|
||||||
m_text_node->set_text_content(m_raw_value);
|
m_text_node->set_text_content(m_raw_value);
|
||||||
|
|
||||||
MUST(m_inner_text_element->append_child(*m_text_node));
|
MUST(m_inner_text_element->append_child(*m_text_node));
|
||||||
MUST(element->append_child(*m_inner_text_element));
|
|
||||||
MUST(shadow_root->append_child(element));
|
update_placeholder_visibility();
|
||||||
set_shadow_root(shadow_root);
|
}
|
||||||
|
|
||||||
|
void HTMLTextAreaElement::update_placeholder_visibility()
|
||||||
|
{
|
||||||
|
if (!m_placeholder_element)
|
||||||
|
return;
|
||||||
|
if (!m_text_node)
|
||||||
|
return;
|
||||||
|
auto placeholder_text = get_attribute(AttributeNames::placeholder);
|
||||||
|
if (placeholder_text.has_value() && m_text_node->data().is_empty()) {
|
||||||
|
MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "block"sv));
|
||||||
|
} else {
|
||||||
|
MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "none"sv));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element:children-changed-steps
|
// https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element:children-changed-steps
|
||||||
|
@ -207,6 +236,16 @@ void HTMLTextAreaElement::children_changed()
|
||||||
m_raw_value = child_text_content();
|
m_raw_value = child_text_content();
|
||||||
if (m_text_node)
|
if (m_text_node)
|
||||||
m_text_node->set_text_content(m_raw_value);
|
m_text_node->set_text_content(m_raw_value);
|
||||||
|
update_placeholder_visibility();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTMLTextAreaElement::attribute_changed(FlyString const& name, Optional<String> const& value)
|
||||||
|
{
|
||||||
|
HTMLElement::attribute_changed(name, value);
|
||||||
|
if (name == HTML::AttributeNames::placeholder) {
|
||||||
|
if (m_placeholder_text_node)
|
||||||
|
m_placeholder_text_node->set_data(value.value_or(String {}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +253,8 @@ void HTMLTextAreaElement::did_edit_text_node(Badge<Web::HTML::BrowsingContext>)
|
||||||
{
|
{
|
||||||
// A textarea element's dirty value flag must be set to true whenever the user interacts with the control in a way that changes the raw value.
|
// A textarea element's dirty value flag must be set to true whenever the user interacts with the control in a way that changes the raw value.
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
|
|
||||||
|
update_placeholder_visibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,8 @@ public:
|
||||||
virtual bool is_auto_capitalize_inheriting() const override { return true; }
|
virtual bool is_auto_capitalize_inheriting() const override { return true; }
|
||||||
|
|
||||||
// ^HTMLElement
|
// ^HTMLElement
|
||||||
|
virtual void attribute_changed(FlyString const&, Optional<String> const&) override;
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/forms.html#category-label
|
// https://html.spec.whatwg.org/multipage/forms.html#category-label
|
||||||
virtual bool is_labelable() const override { return true; }
|
virtual bool is_labelable() const override { return true; }
|
||||||
|
|
||||||
|
@ -94,6 +96,10 @@ private:
|
||||||
|
|
||||||
void create_shadow_tree_if_needed();
|
void create_shadow_tree_if_needed();
|
||||||
|
|
||||||
|
void update_placeholder_visibility();
|
||||||
|
JS::GCPtr<DOM::Element> m_placeholder_element;
|
||||||
|
JS::GCPtr<DOM::Text> m_placeholder_text_node;
|
||||||
|
|
||||||
JS::GCPtr<DOM::Element> m_inner_text_element;
|
JS::GCPtr<DOM::Element> m_inner_text_element;
|
||||||
JS::GCPtr<DOM::Text> m_text_node;
|
JS::GCPtr<DOM::Text> m_text_node;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue