htmx: e.querySelectorAll is not a function - javascript

If I use hx-swap-oob, then I get an error:
<script src="https://unpkg.com/htmx.org#1.3.3/dist/htmx.js"></script>
<div id="sum">?</div>
<table>
<tr>
<td><button
hx-get="https://run.mocky.io/v3/b5a6902e-fc46-479b-92e4-9b4befc7a920"
hx-target="closest tr">1</button>
</td>
<td>one</td>
</tr>
</table>
After pressing "Run Code Snippet" and then press 1:
htmx:swapError
{
"message": "e.querySelectorAll is not a function",
"filename": undefined,
"lineno": undefined,
"colno": undefined
}
The mocky http endpoint returns this:
<tr>
<td>2</td><td>two</td>
</tr>
<div id="sum" hx-swap-oob="true">MAGIC</div>
In above example I use the non minified version, so the error message is: eltOrSelector.querySelectorAll is not a function
If I use this endpoint, it does not fail: https://run.mocky.io/v3/2ab904eb-23a9-4006-b68b-f112b55841f3
But in my usecase the new html fragment should be <tr>...</tr>, not a <div>.....
JS stacktrace:
Uncaught TypeError: eltOrSelector.querySelectorAll is not a function
at findAll (htmx.js:295)
at handleOutOfBandSwaps (htmx.js:501)
at selectAndSwap (htmx.js:712)
at doSwap (htmx.js:2284)
at handleAjaxResponse (htmx.js:2358)
at XMLHttpRequest.xhr.onload (htmx.js:2163)
Update
I could narrow down the issue to this:
This fails:
makeFragment('<tr><td>a</td></tr> <div>X</div>')
Update2
hmmm, now I know why it fails:
parseFromString() of Chrome is the root-cause:
Update3
Follow-up question: Make parseFromString() parse without validation
Update4
I created an issue, hoping someone with more creativity has an idea how to solve this: https://github.com/bigskysoftware/htmx/issues/469

As per your issue, HTMX team has created a config that can be enabled to support table fragments like this. Documented example here: https://htmx.org/examples/update-other-content/#oob. As noted, it is not IE11 compatible.
htmx.config.useTemplateFragments = true;
Execute the following in a js file after htmx has been loaded.

I created a new endpoint which returns a slightly different HTML fragment:
<table>
<tr id="row1">
<td>2</td><td>two</td>
</tr>
</table>
<div id="sum" hx-swap-oob="true">MAGIC</div>
<script src="https://unpkg.com/htmx.org#1.3.3/dist/htmx.js"></script>
<div id="sum">?</div>
before table
<table>
<tr id="row1">
<td><button
hx-get="https://run.mocky.io/v3/28a8e653-491c-4186-abd4-4f48f3579273"
hx-target="#row1" hx-select="#row1">1</button>
</td>
<td>one</td>
</tr>
</table>
after table
HTMX uses parseFromString() internally, and this method validates the input. If there is non valid HTML, it might truncate elements.
makeFragment() of HTMX wraps the string which it sends to parseFromString(). HTMX looks at the first element in the response. In above question it is <tr>. Thus htmx uses <table> to wrap the whole http response.
The solution is the return a "normal" element to htmx (not <tr>).
Normal html fragments in htmx are exactly one element, so that it does not matter. If you use hx-swap-oob the server sends several elements to the client.
This is more or less a work-around. It would be great if htmx would handle the original string.

Related

How to convert a JSON tree to a dynamic HTML table

(First allow me to say that I'm just beginning to learn Node-RED concepts; I went through some beginners' guides at nodered.org, and now am trying to extend what I learned so far).
I'm trying to build a flow that starts with a simple JSON tree like
[{"position":"1", "title":"element #1"},
{"position":"2", "title":"element #2"},
{"position":"3", "title":"element #3"}]
To build that treee I use a template node, property is set to msg.payload.
The number of array elements (in theory) is dynamic. To make sure that this tree is true JSON I added a JSON node converting from String to JSON object.
Next I wish to parse that object into a dynamic html table. For this I used a JS function node that's looping through the object and embedding its elements into the according html elements like this:
var return="";
for(var i=0;i<=msg.payload.length-1;i++){
var row=msg.payload[i];
if(row){
return+="<tr>";
return+="<td>"+row.position+"</td>";
return+="<td>"+row.title+"</td>";
return+="</tr>";
}else{
return+="no object at index "+i.toString();
}
}
msg.payload=return;
return msg;
The output of the function then should be passed into a 2nd template like this:
<html>
<head></head>
<body>
<table border="1">
<tr>
<td>#</td>
<td>Title</td>
</tr>
{{ payload }}
</table>
</body>
</html>
I would have expected that the function's result is inserted into the static table of my template, and that happens indeed but not the way I hoped: somehow the html elements that got created by my function are not recognized as what they shoud be; instead I see that they are rendered as
<tr><td>1</td><
instead of
<tr><td>1</td>
etc.
Result is that the browser does not recognize those elements and prints them together with their contents outside my static table
Questions:
what do I need to do so that my 2nd template recognizes my computed string as a set of html elements?
or is this probably a concept not suitable for my purpose?
I'm assuming you are using handelbars for your templating engine. In that case use:
{{{ payload }}}
Instead of
{{ payload }}
However a more elegant approach would be this:
<html>
<head></head>
<body>
<table border="1">
<tr>
<td>#</td>
<td>Title</td>
</tr>
{{#each payload}}
<tr><td>{{this.position}}</td><td>{{this.title}}</td></tr>
{{/each}}
</table>
</body>
</html>
then just
return msg.payload
again thanks to #als9xd for pointing me into the right direction; his 2nd idea indeed sounds much more elegant but first I couldn't get it to work. After some trial-and-error and looking up documentation for the template node I finally came up with this: removed the function node from my original question and then altered the 2nd template to this code:
<html>
<head></head>
<body>
<table border="1">
<tr>
<td>#</td>
<td>Title</td>
</tr>
{{#payload}}
<tr>
<td>{{position}}</td>
<td>{{title}}</td>
</tr>
{{/payload}}
</table>
</body>
</html>
Difference to #als9xd's example is that I replaced {{#each payload}} with a simple {{#payload}}, plus omitted this when referencing the object keys.
Could this be due to different Node-RED versions?
Anyways this is starting to be much fun!

Selenium IDE fails to find components

I'm testing an ADF application with Selenium IDE. At one point, the automated test case has to click on a button, which has a partialTrigger attribute pointing to a table on the page and after the button executes some background logic, the table is populated with rows, but the page is not fully refreshed.
The problem I'm facing is that the Selenium IDE can't find the table rows after the button click. Possibly, Selenium is not aware of the page's DOM update, but I'm not sure about that.
What I have tried so far is this:
I stored the expected full xpath for a cell in the first row of the table.
Created a self-executing JavaScript function that is used for clicking on the given path.
I have tested the following commands on a simple HTML page and they work fine.
Selenium commands:
<tr>
<td>store</td>
<td>//html[1]/body[1]/div[1]/table[1]/tbody[1]/tr[1]/td[1]</td>
<td>myTableRowPath</td> <!-- store the xpath with name 'myTableRowPath' -->
</tr>
<tr>
<td>storeEval</td>
<td>(function(path) {
var result = selenium.browserbot.getUserWindow()
.document.evaluate(path,
selenium.browserbot.getUserWindow().document,
null,
8,
null).singleNodeValue; result.click();
return null;
})
(${myTableRowPath})
</td>
<td>elementToBeClicked</td>
</tr>
How can I make Selenium IDE aware of any (AJAX) DOM updates on the page ?
Generally when dealing with AJAX on Selenium IDE, you'll want to use a waitForElementPresent type of action.
For instance if your table changes from
<table id="myTable">
<tbody>
<tr>
<td>foo</td>
<td>bar</td>
</tr>
</tbody>
</table>
to
<table id="myTable">
<tbody>
<tr>
<td>foo</td>
<td>bar</td>
</tr>
<tr>
<td>something</td>
<td>else</td>
</tr>
</tbody>
</table>
You can use:
<tr>
<td>click</td>
<td>//input[#id='myButton']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//table[#id='myTable']/tbody/tr[2]</td>
<td></td>
</tr>
<tr>
<td>assertText</td>
<td>//table[#id='myTable']/tbody/tr[2]/td</td>
<td>something</td>
</tr>
Here's a JSFiddle I tested it against (no AJAX, but simulating that with a slow JS function).
ADF manipulate the generated HTML in a way that it'll be very hard to maintain an XPath. The easiest way to target specific elements in ADF is giving this element a styleClass and access it using the class attribute.
ADF loads the table UI (whose ID selenium is looking for) before it loads the contents of the table. It is possible your webdriver is looking for the content before it is loaded, so you could explicitly wait for a short period of time before clicking on the row.
Your table might be using lazy load which only loads the contents of the table when you scroll it into view. If your window only shows 5 rows and there are 10 rows in your table, your browser will only load 5 rows. If you ask selenium to look for row 8, it will throw an exception. I solved this by clicking on the first row and driver.switchTo().activeElement().sendKeys(Keys.DOWN); for the number of times the number of the row I am searching for. So I am "scrolling" to the row before accessing it

jquery not picking up html

So I have the following HTML
<div class="tmpl" id="query_tmpl">
<tr>
<td></td>
<td class="primary_email"></td>
</tr>
</div>
and the following JS:
console.log($('#query_tmpl').html());
For some reason this only logs the 'a' tag. (Ex: http://jsfiddle.net/L8RQq/ )
Why does this happen and how do I get around it so that I can properly pick up the tr/td? I'm using jQuery 1.9.2 if that makes any difference.
Update:
Yes, the markup is 'bad html', but the whole point of this question is how to get around that. Using the HTML present and without altering it, how can I grab the contents even though it's 'bad'?
You don't have table tags around your tr. Try this:
<div class="tmpl" id="query_tmpl">
<table>
<tr>
<td></td>
<td class="primary_email"></td>
</tr>
</table>
</div>
This will log the div's contents properly.
the reason it wasn't logging, is because the browser sees some invalid tr and td tags, and removes those, because they can only be in a table, leaving you with only the a.
If you can't change the markup, tell the person / company that wrote that markup to fix it. It's invalid HTML.
Make it like this
<table class="tmpl" id="query_tmpl">
<tr>
<td></td>
<td class="primary_email"></td>
</tr>
</table>
Then it will surely work correctly
If you want to pick up tr's or td's you can use css manipulation functions such as children([selector]) where selector is eventually a class

AJAX response with TD tags replacing table row is placed nasty in Chrome

I am trying to use editInPlace JavaScript code with Python & Django on Google App Engine.
After editing the row of the table:
<table>
<tr id="editme" class="editme">
<td>Date</td>
<td>Description</td>
<td>Details</td>
</tr>
<tr id="editme" class="editme">
<td>Date</td>
<td>Description</td>
<td>Details</td>
</tr>
<tr id="editme" class="editme">
<td>Date</td>
<td>Description</td>
<td>Details</td>
</tr>
</table>
Which looks like this:
___ ___ ___
|___|___|___|
|___|___|___|
|___|___|___|
I maid that editInPlace JavaScript would save original string like "<td>Date</td><td>Description</td><td>Details</td>" by replacing it with striped string without <td> (ex. "Date Description Details") placing the string in to the <td colspan="3"><form>...</form></td> for editor to edit.
So here I prepared that the Http Response after submitting a new value would also be imitating 3 cols, I mean would have <td></td> tags (ex. "<td>ResponseDate</td><td>ResponseDescription</td><td>ResponseDetails</td>") to be placed in between <tr></tr> tags.
But the problem is such that after AJAX replacing values without refreshing hole page, gives me nasty table.
All the row in Chrome v12 is like moved a side and starts filling from the second col:
___ ___ ___
|___|___|___|___
___|___|___|___|
|___|___|___|
Please use Chrome Developer Tools to inspect the affected row after it has been edited (and displayed in the wrong way) - right-click on any cell and select "Inspect Element" in the popup menu. The DevTools window will show up, and you will be able to examine (in the Elements panel) whether the final DOM is correct. If it is, then it's a Chrome/WebKit bug.
Summary of my problem
After sometime debugging my issue i found my problem was caused by the following situation.
A class with a style content: "" being applied to a target TR prior to an ajax call which would replace the TDs with a fresh set of TDs, then, after removing that class I had the same problem as the OP; The end result was the shifting of the TDs to the right. The HTML on inspection was sound.
In detail this is what I had.
I had a TR which was my targetId container.
I had a TD with an ajax link that then returned a set of TDs to replace the old set within the targetId TR.
I am using jquery ajax and prior to the call I applied a class to the targetId TR, the class of which can be found in this answer and contains the content: "" style.
After the ajax call completes, removing that class.
This is what I ended up doing.
The ajax masking class I was using for the targetId, I replaced with a new class that just did some opacity. I kept the ajax masking class for the sender control.
Relating to the OP's problem
I downloaded and searched the "jquery-editinplace" the OP uses but could not find a content style being applied. Maybe someone with good search tools may find it. As stated in the comments above, the problem disappeared when chrome upgraded. This is my case of it remaining because of something possibly related.
I have not made a fiddle of this situation as I had trouble creating an ajax scenario. I would have liked to to prove it is a chrome bug and to submit it to Google.
Feel free to comment if something is unclear and I will update my answer accordingly.
To me, use same Id on multiple <tr> seems awkward.
It might cause some weird behavior.
Keep your unique Id per DOM.
Also I would use
<div>
Instead of tables
because with <div>, you can get more control with CSS.
HTML:
<div>
<div class="editme">
<div>Date</div>
<div>Description</div>
<div>Details</div>
</div>
<div class="editme">
<div>Date</div>
<div>Description</div>
<div>Details</div>
</div>
<div class="editme">
<div>Date</div>
<div>Description</div>
<div>Details</div>
</div>
</div>
CSS:
.editme { clear:both; }
.editme div { float:left; }
So after changing your HTML and CSS like this
you can simply replace those three divs
with a single DIV with FORM
Here is an example of DIV version

Prototype replace method giving null error

I'm new to prototype, but fairly experienced with jQuery so it could just be I'm misunderstanding how prototype works. I'm trying to do the following
$$("tr.total_line td.total_cell").first().replace(newTotal);
but I'm getting TypeError: Cannot read property 'firstChild' of null
When I execute $$("tr.total_line td.total_cell").first() in the JS console I get a DOM element result.
Here's the relevant markup
<tr class="total_line">
<td colspan="2">Total</td>
<td class="total_cell">$50.00</td>
<td></td>
</tr>
Since .first() is returning a <td> element, you'll need to insert a <td> or <th> in order for the replacement to be valid HTML.
So if newTotal is:
"<td>$100</td>"
...it should work. But if it is just:
"$100"
...it doesn't.
Or another option would be to replace the innerHTML of the <td>:
$$("tr.total_line td.total_cell").first().innerHTML = newTotal;
If replace continues to cause problems you can try update instead which works in a similar way.

Categories