+sub val {
+  my $self = shift;
+
+  # Element
+  return $self->_val unless $self->type eq 'form';
+
+  # Form
+  my ($form, $val) = ({});
+  for my $e ($self->find('select, textarea')->each) {
+    $form->{$e->{name}} = $val if defined($val = $e->_val);
+  }
+  for my $e ($self->find('input')->each) {
+    next if +(grep { $_ eq $e->{type} || '' } qw( radio checkbox )) && !defined $self->{checked};
+    $form->{$e->{name}} = $val if defined($val = $e->_val);
+  }
+
+  return %$form ? $form : undef;
+}
+
 sub wrap         { shift->_wrap(0, @_) }
 sub wrap_content { shift->_wrap(1, @_) }
 
@@ -353,6 +372,25 @@ sub _text {
   return $text;
 }
 
+sub _val {
+  my $self = shift;
+
+  # "option"
+  return $self->{value} // $self->text if $self->type eq 'option';
+
+  # "textarea"
+  return $self->text if $self->type eq 'textarea';
+
+  # "select"
+  if ($self->type eq 'select') {
+    my @values = map { $_->val } $self->find('option[selected]')->each;
+    return !@values ? undef : @values == 1 ? $values[1] : \@values;
+  }
+
jhthorsen mojo/mojo-dom-val$ git diff HEAD^^^|cat
diff --git a/lib/Mojo/DOM.pm b/lib/Mojo/DOM.pm
index 927d5e8..c67aa7e 100644
--- a/lib/Mojo/DOM.pm
+++ b/lib/Mojo/DOM.pm
@@ -176,6 +176,25 @@ sub type {
   return $self;
 }
 
+sub val {
+  my $self = shift;
+
+  # Element
+  return $self->_val unless $self->type eq 'form';
+
+  # Form
+  my ($form, $val) = ({});
+  for my $e ($self->find('select, textarea')->each) {
+    $form->{$e->{name}} = $val if defined($val = $e->_val);
+  }
+  for my $e ($self->find('input')->each) {
+    next if +(grep { $_ eq $e->{type} || '' } qw( radio checkbox )) && !defined $self->{checked};
+    $form->{$e->{name}} = $val if defined($val = $e->_val);
+  }
+
+  return %$form ? $form : undef;
+}
+
 sub wrap         { shift->_wrap(0, @_) }
 sub wrap_content { shift->_wrap(1, @_) }
 
@@ -353,6 +372,25 @@ sub _text {
   return $text;
 }
 
+sub _val {
+  my $self = shift;
+
+  # "option"
+  return $self->{value} // $self->text if $self->type eq 'option';
+
+  # "textarea"
+  return $self->text if $self->type eq 'textarea';
+
+  # "select"
+  if ($self->type eq 'select') {
+    my @values = map { $_->val } $self->find('option[selected]')->each;
+    return !@values ? undef : @values == 1 ? $values[1] : \@values;
+  }
+
+  # "input"
+  return $self->{value};
+}
+
 sub _wrap {
   my ($self, $content, $new) = @_;
 
@@ -824,6 +862,23 @@ This element's type.
   # List types of child elements
   say $dom->children->type;
 
+=head2 val
+
+  my $array_ref = $dom->at('select')->val;
+  my $hash      = $dom->at('form')->val;
+  my $str       = $dom->at('input')->val;
+
+Extract values from C<form>, C<input>, C<select>, C<option> and C<textarea>
+elements or return C<undef>.
+
+In case of C<select> this method will find all the C<option>s that is selected
+and return them in an array-ref with one or more elements. Returns C<undef> if
+no options has been selected.
+
+In case of C<form> it will fetch the values from all the child field elements
+that is checked, selected or has a value and return them as in a hash-ref
+structure, where the keys are field names.
+
 =head2 wrap
 
   $dom = $dom->wrap('<div></div>');