ron tornambe ron tornambe - 4 months ago 14
Javascript Question

Why is this jQuery selector not returning any elements?

I am trying to select all sub-elements and filter those with certain attributes. One of the elements is a contenteditible div that contains a data-required attribute(see HTML below). When I use the following selector, it returns 1:

$("#mail-container").filter('[required], [data-required]').length


But when I execute the following, 0 is returned:

form.contents().filter('[data-required]').length;


Note, the following returns 9:

form.contents().length;


EDIT

The following function is called as follows:

$("#send-email").on('mouseup', function (e) {
if (!validateRequiredFields($("#form-email-preview"))) return false;
var data = {};
data.action = "manuscript-request-email";
data["writers-id"] = $("#writer-id").val();
data["account-id"] = localStorage.getItem("account-id");
data.subject = $("#subject").val();
data.body = $("#mail-container").html();
ajax('post', 'php/writers.php', data, sentEmail);
function sentEmail(result) {
console.log("email-res=" + result);
return2table();
}
});

function validateRequiredFields(form) {
var $reqFlds = form.contents().filter('[required], [data-required]');
var ret = true;
debugger
$reqFlds.each(function() {
var $this = $(this);
var result = ($this.eq("input, select, textarea")) ? $this.val() : $this.text();
if (!$.trim(result)) {
$this.prop('data-toggle=popover', true);
$this.show();
$this.popover("destroy").popover({ content: "Please fill out this field.", placement: 'bottom' });
$this.popover('show');
$this.focus();
ret = false;
return false;
}
});
return ret;
}


HTML

<form id="form-email-preview" style="margin-top: 24px;">
<div id="main-container" class="hidden" style="border: 1px solid black; padding: 6px; border-radius: 8px;">
<span><b>Subject:</b></span><input id="subject" type="text" value="Manuscript Request" style="display: inline-block" />
<p><b>Email:</b></p>
<div id="mail-container" contenteditable data-toggle="popover" data-required data-placement="top" data-title ="Required field" style="display: inline-block"></div><br/>
<p id="buttons" class="hidden">
<input type='button' id='send-email' class='btn btn-custom-success' value='Send Email' /><span> </span>
<button id='cancel-email' class = 'btn btn-danger'>Cancel</button>
</p>
</div>
<div id="current-row" class="hidden"></div>
<input id="current-checkbox" type="checkbox" class="hidden" />
<input id="current-tr" type="number" class="hidden" />
</form>


What am I missing?

Answer

There simply are no matching elements ?

In this context, you're doing

validateRequiredFields( $("#form-email-preview") );

function validateRequiredFields(form) {

    var $reqFlds =  form.contents().filter('[required], [data-required]');

    ....

form.contents() gets you the nine children of the form, that's including the textnodes

<form id="form-email-preview" style="margin-top: 24px;">
    <!-- text node here -->                                           <!-- 1 -->
    <div id="main-container" ...></div>                               <!-- 2 -->
    <!-- text node here -->                                           <!-- 3 -->
    <div id="current-row" class="hidden"></div>                       <!-- 4 -->
    <!-- text node here -->                                           <!-- 5 -->
    <input id="current-checkbox" type="checkbox" class="hidden" />    <!-- 6 -->
    <!-- text node here -->                                           <!-- 7 -->
    <input id="current-tr" type="number" class="hidden" />            <!-- 8 -->
    <!-- text node here -->                                           <!-- 9 -->
</form>

That's nine, none of them are the elements you're looking for, and here's what contents() does

Get the children of each element in the set of matched elements, including text and comment nodes.

Then you filter those nine elements, and only those nine, as filter() doesn't filter children of those nine, only those

Reduce the set of matched elements to those that match the selector or pass the function's test.

You have a set of nine elements, none of them with the attributes you're looking for, and you filter that set, and you end up with ... nothing

What you wanted was just

var $reqFlds = form.find('[required], [data-required]');

As a sidenote, read the documentation for the methods you're using, and it should be clear what they do, for instance these are incorrect

$this.eq("input, select, textarea") // eq() accepts a number only

$this.prop('data-toggle=popover', true); // accepts a property, 
                                         // not an entire attribute, with the value ?
Comments