babel + react + jsx renders strange markup - javascript

I use react 0.13.3 with babel 5.8.26. I noticed it started rendering strange markup. This is what I have in js file:
<p className="navbar-text navbar-right dropdown hidden-xs">
<a className="navbar-link dropdown-toggle" href="#" id="accountddl" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"><i className="st st-profile st-2"></i></a>
<ul className="dropdown-menu" aria-labelledby="accountddl">
<li>Action</li>
</ul>
</p>
You can see the output there.
But what it renders back is this:
<p class="navbar-text navbar-right dropdown hidden-xs" data-reactid=".0">
<a class="navbar-link dropdown-toggle" href="#" id="accountddl" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true" data-reactid=".0.0">
<i class="st st-profile st-2" data-reactid=".0.0.0"/>
</a>
</p>
<ul class="dropdown-menu" aria-labelledby="accountddl" data-reactid=".0.1">
<li data-reactid=".0.1.0">
Action
</li>
</ul>
<p/>
As you can see it closes the p tag before rendering ul and in the end it attaches a self-closed p tag.
How to make ul a part of one parent p tag?
Am I doing something wrong?
If I change p to div it works as expected - result
Update
Thanks to the guys comments. I realized that this is not valid HTML. React is good and does its job well. So, when the browser sees the case, it decides to break it the way shown above.

The HTML spec defines rules for how markup may be nested. In this case:
<p> is:
Content categories: Flow content, palpable content.
Permitted content: Phrasing content.
<ul> is:
Content categories: Flow content
Permitted parent elements: any element that accept flowing content
which translates to <ul> elements not being allowed within <p> elements. To avoid this, React and/or the browser will basically pull the <ul> one level up in the DOM and split the <p> around it, resulting in what you are seeing.
Update
From the release notes of the newly released React 0.14:
React DOM now warns you when nesting HTML elements invalidly, which helps you avoid surprising errors during updates.

Related

Dynamic css multi column layout - update columns / force reflow?

TLDR: Can I force a column layout refresh after dynamically updating the 'css-column' css with javascript/jquery?
I have a navigation menu that I am using css columns for (I cannot work out how to use the Magento2 menu column functionality). I cannot really change the markup much. I want to stop 'widow' titles from happening (titles at the bottom of the column, with all the content on the next column - see 3rd pink title in screenshot below). It cannot be set with css as the nav items are dynamic and will change.
(I have tried "break-after:avoid" on the parent list, but this stops the list from breaking altogether - i.e. it should be able to break anywhere in the middle of the list).
I came up with a hacky solution that almost works - with javascript I am dynamically adding "break-before: column" to any list that has a widow title. (It checks the left offset of the title, compared with the offset of the first list child. If it is different then it means the title starts on the previous column).
This (sometimes) works BUT it (sometimes) does not update the columns until you resize the screen a bit.
I have been been trying out these kinds of solutions and many of these too. However none of them seem to work, for example I have tried:
console.log(elt.offsetHeight);
console.log(elt.getBoundingClientRect());
Tried 'elt' being all sorts, the parent elements, the whole body/doc wrapper etc. I thought maybe this was because the element in question is hidden until you hover over the nav link. But I forced it to be open using chrome devtools and ran the same 'reflow triggering' javascript and it still made no difference?
Is there a (preferably nice and not forcing reflow of the whole document every pageload??) way to force a recalculation of the css columns?
OR is a better solution possible using css grid / any other way?
Thanks!
In case it is relevant to another possible solution, the markup is like this:
<ul class="level0 submenu">
<li class="navigation_item__back hidden-desktop">
Back
</li>
<li class="navigation_item__link hidden-desktop">
Title
</li>
<li class="level1 nav-3-1 category-item first parent">
<a href="">
<span>Sub Title</span>
</a>
<ul class="level1 submenu">
<li class="navigation_item__back hidden-desktop">
Back
</li>
<li class="navigation_item__link hidden-desktop">
Sub Title
</li>
<li class="level2 nav-3-1-1 category-item first">
<a href="/">
<span>Lorem 1psum</span>
</a>
</li>
<li class="level2 nav-3-1-2 category-item">
<a href="/">
<span>Lorem 2psum</span>
</a>
</li>
</ul>
</li>
...
</ul>

Bootstrap dropdown: Anchor tag used as Bootstrap dropdown button keeps its default behaviour

I have the following code where I use an Anchor tag instead of a button in my Bootstrap dropdown. The problem is that, despite what is said here, when I click on the Anchor tag (labeled Action) the browser opens the Anchor tag instead of just opening the menu. In other words, the default behavior of the Anchor tag is not prevented.
We use Bootstrap v3 and this does not happen in all the environments, only in prod for now and sometimes on my local machines. This happens accross all the known browsers i.e. Chrome, IE, FF for the desktop version, the non-desktop version does not have this functionality, so we did not test it there.
<div class="dropdown btn-group">
<a class="btn dropdown-toggle" role="button" data-toggle="dropdown" href="#">
Action <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>Foo</li>
<li>Bar</li>
</ul>
Finally we used the following line of code to cancel out the default behaviour, but we never figured out the root cause of this happening only in one environment not in others:
$(".btn .dropdown-toggle").click(function(event){ event.preventDefault();})
Why do you need href="#"?
Use href="" instead of href="#".
Try removing from the code
role="button"
why is it an anchor?
you've missed out the data-target attribute as shown in the bootstrap documentation
https://getbootstrap.com/docs/3.3/javascript/#dropdowns

how to locate dynamic elements in `protractor`

I get this error when I try to run the code below:
Failed: element not visible
Following is the html code for the elements I want to locate:
<a class="dropdown-toggle ng-binding ng-scope" aria-expanded="false"
role="button" href="/" data-toggle="dropdown" ng-if="tab.subtabs">
Content
<span class="caret"></span></a>
<ul class="dropdown-menu ng-scope" role="menu" ng-if="tab.subtabs">
<li class="ng-scope" ng-repeat="test in tab.subtabs">
<a class="ng-binding" target="_self" href="/custom/ui">Custom</a>
</li>
<li class="ng-scope" ng-repeat="test in tab.subtabs">
<a class="ng-binding" target="_self" href="/patch/ui">Patch</a></li>
<li class="ng-scope" ng-repeat="test in tab.subtabs">
<a class="ng-binding" target="_self" href="/swd/ui">Software</a>
I want to click on element within the tags:
<a class="ng-binding" target="_self" href="/custom/ui">Custom</a>
<a class="ng-binding" target="_self" href="/patch/ui">Patch</a>
<a class="ng-binding" target="_self" href="/swd/ui">Software</a>
I have tried following code in protractor, but it is not working:
it("should click on Content and then custom", function(){
element(by.xpath('/html/body/bf-header/div/nav/div/div/div[2]/ul[1]
/li[2]/a')).element(by.xpath('/html/body/bf-header/div/nav/div/div
/div[2]/ul[1]/li[3]/ul/li[1]')).click();
element(by.xpath('/html/body/bf-header/div/nav/div/div/div[2]/ul[1]
/li[2]/a')).element(by.xpath('/html/body/bf-header/div/nav/div/div
/div[2]/ul[1]/li[3]/ul/li[1]')).click();
Well, now you can probably see why the Protractor Style Guide recommends not to ever use XPath location technique:
NEVER use xpath
Why?
It's the slowest and most brittle locator strategy of all
Markup is
very easily subject to change and therefore xpath locators require a
lot of maintenance
xpath expressions are unreadable and very hard to
debug
It is not always true and, if you choose XPath, the expressions you make must at least really be simple and readable. The first thing to fix, if you are gonna stay with XPaths, is not to make absolute XPath expressions - you don't have to start with html element and check every single element going down the tree to the desired element.
In this case, simple "by link text" or "by partial link text" locators should work perfectly:
element(by.linkText("Custom")).click();
Note that the Failed: element not visible error might be thrown, because you don't click the menu to open it up, before trying to click the submenu (assuming these are the submenu links you need to click).
You might also need to wait for the element to be clickable:
var EC = protractor.ExpectedConditions;
var custom = element(by.linkText("Custom"));
browser.wait(Ec.elementToBeClickable(custom), 5000);
custom.click();
Hope that helps.

Javascript Create and Add div vs Clone, Modify and Add div

I want to dynamically add divs to my html page. The server sends a list containing the name, description, dates etc. for a task. For each task, a div looking like this, has to be added to the body. So, I have 2 methods of creating it -
Clone the code skeleton shown below ( using cloneNode() ),
Use something like div.childNode[3].childNode[1].textContent = "Task Name" to update each text in it,
Append it to the parent ( using parent.appendChild() )
Create a div ( using createElement() ),
Add classes and styles to it,
Add children containing task name, description, dates etc. to it,
Append it to the parent.
Considering that the list server is going to send can contain up to 100 tasks, so performance wise which method is better ?
Also, are there other ways to do so ?
<div class="task-row min-width900" id="task_row" style="display: none">
<div class="div-inline pull-left min-width25" style="margin-left: 5px">
<input type="checkbox" class="checkbox" id="task_row_checkbox">
</div>
<div class="div-inline pull-left min-width30" style="margin-right: 2px; border-right: 1px solid #dedede;">
<a data-toggle="collapse" data-target="#collapse11" href="#" style="text-decoration:none; color:#4d4d4d; font-size: 20px;" id="subtask_count">
+2
</a>
</div>
<div class="dropdown div-inline pull-left min-width300" style="width: 26%;">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" style="text-decoration:none; color:#4d4d4d; font-size: 15px;" id="name_link">
<strong id="name">Summer's here: Lets clean the house</strong>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
<li><i class="icon-ok"></i> Done</li>
<li><i class="icon-minus-sign"></i> Dismissed</li>
<li><i class="icon-trash"></i> Delete</li>
</ul>
</div>
<div style="width: 15%;" class="div-inline min-width200 task-tags" id="tags">
<a href="#" style="text-decoration:none">
<span class="label label-important">Work</span>
</a>
<a href="#" style="text-decoration:none">
<span class="label label-warning">Downtown</span>
</a>
</div>
<div class="div-inline task-description" style="width: 32%">
<a href="#" style="text-decoration:none">
<span class="muted" id="description">It's that time of the year again ...</span>
</a>
</div>
<div style="width: 10%" class="div-inline min-width130 pull-right">
<a href="#" style="text-decoration:none; color:#4D4D4D;">
<strong style="font-size: 30px;" id="due_date">13.09.12</strong>
</a>
</div>
<div style="width: 10%" class="div-inline min-width80 pull-right">
<a href="#" style="text-decoration:none; color:#4d4d4d;">
<strong id="start_date">2 days ago</strong>
</a>
</div>
</div>
Thanks
Update:
I am using Django framework for this application ( the task row is a part of it ). So, is adding divs via JS better performance-wise or should Django's {% for loop %} be used to add the task-rows to the page ?
I agree that a JS templating engine might be the best solution.
As for performance, if you still want to do it on your own, regardless of whether you choose to clone or create, most important thing for doing that for 100 items in a loop is, don’t append each element to the document inside the loop, but use a DocumentFragment instead: Append the divs to the fragment, one after another, and after you are done with looping through your items append the fragment, that now contains all of those 100 items, to your document.
(A DocumentFragment is just a “soulless” temporary container – when you finally append the fragment to the document, it will just “dissolve” without leaving a trace, so the effect will be the same as if you had appended the 100 elements individually. As to why it improves performance, see https://stackoverflow.com/a/13771168/1427878.)
In my current project I'm dynamically creating about 600 elements, each with about 10 nested children. They're mostly identical aside from a text node and an src attribute of an image. The best pure-JS way I've found to create and modify these elements takes 10-15ms and works as follows:
Create a DocumentFragment.
Use the fragment to build a template.
Deep clone the fragment.
Modify the clone.
Create the actual root element.
Append the clone to the root element.
Repeat 3-6 as many times as necessary.
According to my tests on Chrome 66/Windows (https://jsperf.com/creating-multiple-nested-elements/1), this is about 10x faster than creating each element from scratch, and about 3x faster than using a non-DocumentFragment as the template root. I would imagine that the benefit becomes greater the more complex the structure you want to clone is.
According to this, cloneNode is slightly better performance:
http://jsperf.com/clonenode-vs-createelement-performance/2
However, I really do suggest you take a look at something like KnockoutJS:
http://knockoutjs.com/examples/simpleList.html
It will save your sanity in the long run.
It's faster to clone the div.
Check out this performance test.

Simulate a click on tabpanel in ExtJs

Let me begin by saying that this problem is specific to TabPanel in ExtJS library.
I can successfuly simulate clicks on other ExtJS components using jQuery("#id").click() function.
But I cannot simulate user click on a given tab of TabPanel in ExtJS using jQuery. I see that the dom structure behind tabs looks like this:
<ul class="x-tab-strip x-tab-strip-top" id="ext-gen15">
<li id="ext-comp-1009__ext-comp-1001" class="x-tab-strip-active">
<a class="x-tab-strip-close" id="ext-gen18"></a>
<a href="#" class="x-tab-right" id="ext-gen19">
<em class="x-tab-left">
<span class="x-tab-strip-inner">
<span class="x-tab-strip-text">Submarine</span>
</span>
</em>
</a>
</li>
<li id="ext-comp-1009__ext-comp-1003" class="">
<a class="x-tab-strip-close" id="ext-gen20"></a>
<a href="#" class="x-tab-right" id="ext-gen21">
<em class="x-tab-left">
<span class="x-tab-strip-inner">
<span class="x-tab-strip-text">SpaceShuttle</span>
</span>
</em>
</a>
</li>
<li class="x-tab-edge" id="ext-gen16">
<span class="x-tab-strip-text"> </span>
</li>
<div class="x-clear" id="ext-gen17"></div>
</ul>
I've tried various methods of selecting the first tab using jQuery:
jQuery("#ext-comp-1009__ext-comp-1001").click();
jQuery("#ext-gen18").click();
jQuery("#ext-gen19").click();
but none of it seem to work.
If you take a look at the source (search for onStripMouseDown in particular) you'll see that it's expecting an Ext.EventObject. It'll fail straight away because e.button isn't 0.
Presumably you could patch it with your own onStripMouseDown.

Categories

Resources