diff --git a/lib/Mojo/DOM.pm b/lib/Mojo/DOM.pm
index 927d5e8..3934480 100644
--- a/lib/Mojo/DOM.pm
+++ b/lib/Mojo/DOM.pm
@@ -176,6 +176,28 @@ sub type {
   return $self;
 }
 
+sub val {
+  my $self = shift;
+
+  # Element
+  return $self->_val unless $self->type eq 'form';
+
+  # Form
+  my $form = {};
+  for my $e ($self->find('select, textarea'))->each) {
+    next unless defined(my $val = $e->_val);
+    $form->{$e->{name}} = $val;
+  }
+  for my $e ($self->find('input')->each) {
+    my $type = $e->{type} || '';
+    next if ($type eq 'radio' || $type eq 'checkbox') && !defined $self->{checked};
+    next unless defined(my $val = $e->_val);
+    $form->{$e->{name}} = $val;
+  }
+
+  return $form;
+}
+
 sub wrap         { shift->_wrap(0, @_) }
 sub wrap_content { shift->_wrap(1, @_) }
 
@@ -353,6 +375,23 @@ sub _text {
   return $text;
 }
 
+sub _val {
+  my $self = shift;
+
+  # "textarea"
+  return $self->text if $self->type eq 'textarea';
+
+  # "select"
+  return [map { $_->{value} } $self->find('option[selected]')->each]
+    if $self->type eq 'select';
+
+  # "option"
+  return $self->{value} || $self->text if $self->type eq 'option';
+
+  # "input"
+  return $self->{value};
+}
+
 sub _wrap {
   my ($self, $content, $new) = @_;
 
@@ -824,6 +863,22 @@ 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 zero or more elements.
+
+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>');