diff --git a/Changes b/Changes
index 3a65845..89bd675 100644
--- a/Changes
+++ b/Changes
@@ -1,6 +1,7 @@
 
 5.17  2014-07-23
   - Welcome to the Mojolicious core team Jan Henning Thorsen.
+  - Added val method to Mojo::DOM. (batman, sri)
 
 5.16  2014-07-21
   - Improved Mojo::Asset::File to allow appending data to existing files.
diff --git a/lib/Mojo/DOM.pm b/lib/Mojo/DOM.pm
index 927d5e8..fd11fc8 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 = {};
+  _pair($form, $_->{name}, $_->_val) for $self->find('select, textarea')->each;
+  _input($form, $_)
+    for $self->find('input:not([type=button], [type=submit], [type="reset"])')
+    ->each;
+  my $e = $self->at(
+    'button[type="submit"], button:not([type]), input[type=submit]');
+  _input($form, $e) if $e;
+
+  return $form;
+}
+
 sub wrap         { shift->_wrap(0, @_) }
 sub wrap_content { shift->_wrap(1, @_) }
 
@@ -253,6 +272,14 @@ sub _delegate {
   return $self;
 }
 
+sub _input {
+  my ($form, $e) = @_;
+  my $type = $e->{type} // '';
+  my $check = $type eq 'radio' || $type eq 'checkbox';
+  return if $type eq 'button' || ($check && !defined $e->{checked});
+  _pair($form, $e->{name}, $e->_val);
+}
+
 sub _link {
   my ($children, $parent) = @_;
 
@@ -280,6 +307,8 @@ sub _offset {
   return $i;
 }
 
+sub _pair { $_[0]->{$_[1]} = $_[2] if defined $_[1] && defined $_[2] }
+
 sub _parent { $_[0]->tree->[$_[0]->node eq 'tag' ? 3 : 2] }
 
 sub _parse { Mojo::DOM::HTML->new(xml => shift->xml)->parse(shift)->tree }
@@ -353,6 +382,26 @@ sub _text {
   return $text;
 }
 
+sub _val {
+  my $self = shift;
+
+  # "option"
+  my $type = $self->type;
+  return $self->{value} // $self->text if $type eq 'option';
+
+  # "select"
+  if ($type eq 'select') {
+    my @values = $self->find('option[selected]')->val->each;
+    return @values ? @values > 1 ? \@values : $values[0] : undef;
+  }
+
+  # "textarea"
+  return $self->text if $type eq 'textarea';
+
+  # "input" or "button"
+  return $type eq 'input' || $type eq 'button' ? $self->{value} : undef;
+}
+
 sub _wrap {
   my ($self, $content, $new) = @_;
 
@@ -824,6 +873,25 @@ This element's type.
   # List types of child elements
   say $dom->children->type;
 
+=head2 val
+
+  my $value = $dom->val;
+
+Extract values from C<button>, C<form>, C<input>, C<option>, C<select> and
+C<textarea> elements or return C<undef> if this element has no value. In the
+case of C<select>, find an C<option> element with C<selected> attribute and
+return its value or an array reference with all values if there are more than
+one. In the case of C<form>, find all elements mentioned before that would be
+submitted by pressing the first button and return a hash reference with their
+names and values.
+
+  # "b"
+  $dom->parse('<form><input name="a" value="b"></form>')->at('input')->val;
+
+  # "b"
+  $dom->parse('<form action="/"><input name="a" value="b"></form>')
+    ->at('form')->val->{a};
+
 =head2 wrap
 
   $dom = $dom->wrap('<div></div>');
diff --git a/t/mojo/dom.t b/t/mojo/dom.t
index b90c7af..60f163f 100644
--- a/t/mojo/dom.t
+++ b/t/mojo/dom.t
@@ -2303,6 +2303,68 @@ is $dom->find('div > ul li')->[2], undef, 'no result';
 is $dom->find('div > ul ul')->[0]->text, 'C', 'right text';
 is $dom->find('div > ul ul')->[1], undef, 'no result';
 
+# Form values
+$dom = Mojo::DOM->new(<<EOF);
+<form action="/foo">
+  <p>Test</p>
+  <input type="text" name="a" value="A" />
+  <input type="checkbox" checked name="b" value="B">
+  <input type="checkbox" name="c" value="C">
+  <input type="radio" name="d" value="D">
+  <input type="radio" checked name="e" value="E">
+  <select name="f">
+    <option value="F">G</option>
+    <optgroup>
+      <option>H</option>
+      <option selected>I</option>
+    </optgroup>
+    <option value="J" selected>K</option>
+  </select>
+  <select name="n"><option>N</option></select>
+  <select name="l"><option selected>L</option></select>
+  <textarea name="m">M</textarea>
+  <input type="button" name="s" value="S" />
+  <button name="o" value="O">No!</button>
+  <input type="submit" name="p" value="P" />
+</form>
+<form id="button">
+  <button type="reset" name="r" value="R">No!</button>
+  <button type="button" name="b" value="B">No!</button>
+  <button type="submit" name="s" value="S">Yes!</button>
+</form>
+<form id="input">
+  <input type="button" name="b" value="B">
+  <input type="reset" name="r" value="R">
+  <input type="submit" name="q" value="Q">
+</form>
+<form id="empty"></form>
+EOF
+is $dom->at('p')->val, undef, 'no value';
+is_deeply $dom->at('form')->val,
+  {a => 'A', f => ['I', 'J'], l => 'L', m => 'M', o => 'O'},
+  'val() for form /foo';
+is $dom->at('input')->val, 'A', 'first input val';
+is $dom->find('input')->[2]->val, 'C', 'third input val';
+is $dom->find('input')->[3]->val, 'D', 'fourth input val';
+is_deeply $dom->find('select')->first->val, ['I', 'J'], 'select val';
+is $dom->at('select option')->val, 'F', 'option val';
+is $dom->find('select')->[1]->val, undef, 'no val';
+is $dom->find('select')->[1]->at('option')->val, 'N',
+  'second select option val';
+is $dom->find('select')->last->val, 'L', 'last select val';
+is $dom->at('textarea')->val, 'M', 'textarea val';
+is $dom->at('form')->find('input')->[-2]->val, 'S', 'val for input -2';
+is $dom->at('form')->find('input')->last->val, 'P', 'last input val';
+is_deeply $dom->at('form#button')->val, {s => 'S'},
+  'button button is for javascript';
+is_deeply $dom->at('form#button')->at('[type="button"]')->val, 'B',
+  'val for direct button button';
+is_deeply $dom->at('form#input')->val, {q => 'Q'},
+  'input button is for javascript';
+is_deeply $dom->at('form#input')->at('[type="button"]')->val, 'B',
+  'val for direct input button';
+is_deeply $dom->at('form#empty')->val, {}, 'empty hash for empty form';
+
 # Slash between attributes
 $dom = Mojo::DOM->new('<input /type=checkbox / value="/a/" checked/><br/>');
 is_deeply $dom->at('input')->attr,
@@ -2372,4 +2434,4 @@ $dom = Mojo::DOM->new($huge);
 is $dom->all_text, 'works', 'right text';
 is "$dom", $huge, 'right result';
 
-done_testing();
+done_testing;