jQuery li:has(ul) selector issue - jquery-selectors

I was creating a tree using UL LI list and jQuery. I used a jQuery Selector
jQuery(li:has(ul)) to find all the list nodes having childs and then added a click event to it.
jQuery(li:has(ul)).click(function(event) {
jQuery(this).children.toggle();
jQuery(this).css("cursor","hand");
});
This works for me except i dont understand why i get a cursor hand and click event triggering even when i take mouse pointer to childs of my selected li
<li> Parent // it works here that is fine
<ul>
<li> child1 // it works here i dont understand need explanation
</li>
<li> child2 // it works here i dont understand need explanation
</li>
</ul>
</li>

What you are seeing is event bubbling. When you click on a descendant element, the event also bubbles up to its ancestors. Your click handler on the ancestor element is what is being triggered. If you don't want this, you'll need to also stop event propagation in the click handlers of the child elements to prevent the event from bubbling up.
jQuery(li:has(ul)).children().find('li').click(function(e) {
e.stopPropagation();
});

The cursor property and the click event are inherited by the element's children.
To prevent the cursor property from being inherited, you can add a CSS rule that explicitly sets the cursor for <ul> and <li> elements, like this:
ul, li {
cursor: default;
}
To prevent the click event from firing for the children, you can check event.target, like this:
if (event.target !== this) return;

yea try
jQuery('ul',this).css("cursor","hand !important"); .// to tell it to apply the cursor to the ul elements with "this" i.e the original li element

Related

How to prevent tinymce from adding class to every new li in list

I'm using the lists plugin from tinymce.
I have made a function that will add a class to one of the li elements in the list.
If I add the class to the last element in the list, every new li element added by the editor when hitting enter will have the same class.
I've tried looking into content filtering, but there it looks like I can only prevent any li element having a class.
How can I make tinymce add new li elements without a class?
What happens now:
<ul>
<li>asd</li>
<li>asd</li>
<li class="optional">asd</li> (updated by right clicking and selecting optional)
<li class="optional">asd</li> (new elements when hitting enter)
<li class="optional">asd</li>
<li class="optional">asd</li>
</ul>
What I want
<ul>
<li>asd</li>
<li>asd</li>
<li class="optional">asd</li> (updated by right clicking and selecting optional)
<li>asd</li> (new elements when hitting enter)
<li>asd</li>
<li>asd</li>
</ul>
Thanks in advance!
There is a configuration option that tells TinyMCE to not bring styles from one root block element (<p>, <li> etc) to another: keep_styles.
The documentation is here: https://www.tiny.cloud/docs/configure/content-filtering/#keep_styles
The setting either takes true (default) or false. As an example:
keep_styles: false
If you set this to false you won't get the current list item's classes/styles brought down to the next list item.
Here is a running example: https://fiddle.tiny.cloud/dNhaab

Dynamically adding Semantic UI modals with SilverStripe

I'm having a hard time trying to connect SUI modal with SilverStripe to generate them dynamically.
I want to achieve something like this:
I have button (attach events) to trigger modal with some content. I wanted to loop that elements (GridField) to generate everything dynamically. But the only thing I can achieve is multiple modals with the same "trigger class" and it doesn't matter which button I have clicked (first, last or whatever). I only have one modal (the last in hierarchy). Without SUI the solution is easy - put "this" to fetch the closest element and I can have as many different modals as I want, but SUI seems to complicate things.
I think the best solution will be to generate a class/id with SilverStripe and pass it to the JavaScript to use with modal or use one class for every modal and to "somehow inform" that this button triggers this modal.
Basically I need one code snippet to handle many modals, not many snippets to handle many modals. I hope it's clear enough what the the problem is.
Here is my code:
HTML/SS
(without specific SilverStripe tags)
<% loop SomeName %>
<div class="job-offers">
<a class="ui right floated button job-application">Click here</a>
</div>
<div class="ui basic modal job-application">
<div class="job-application-sheet">
(...)
<div class="job-application-sheet-content">
<div class="ui grid">
(...)
<div class="ui center aligned container job-application-action">
<p>Lorem ipsum</p>
<button class="ui primary button">Click here</button>
</div>
</div>
</div>
</div>
</div>
<% end_loop %>
JavaScript
$('.ui.basic.modal.job-application')
.modal({
autofocus : false,
observeChanges: true
})
.modal('attach events', '.job-application', 'show');
As you can see "job-application" class is a trigger for modal, so is this possible to change it to "(this)" so I don't have to write "specific" class for each button/modal. Or maybe there is a different/easier solution?
first i generated a data-type attribute for each of my elements that are going to display a modal when they are clicked, and in send as a local the same index to the modal this way:
<% #relateds_array.each.with_index do |related,i| %>
<div class="card custom" data-id="<%=i%>">
<%= render partial: 'my_modal', locals: {index: i} %>
</div>
<% end %>
the i put the modal in the partial that i called my_modal for this example and used the index that i sent as a local to create an unique id for each my modals, like this:
<div class="ui modal" id="modal-<%=index%>">
<p>blabla things inside this modal.</p>
</div>
and then with javascript, simply get the data-id of the element clicked and select the element that contain the id for that data-id, like this:
$('.element_that_you_want_to_open_moda_on_click').click(function(event){
var card_clicked = $(this).attr('data-id');
$('#modal-' + card_clicked).modal('show');
});
Hope this was useful for you.
Note that i used Ruby on Rails for this example, but the behaviour that you should use should be something similar to this, no matter what framework you use.
Answer based on Sebastian's solution. I did some minor tweaks to meet my needs, like I've used ID variable to automatically get DataObject ID which is generated dynamically.
Basically to dynamically add Semantic UI (SUI) modal in SilverStripe, first you should add "data-id" in a modal trigger, for example:
Template.ss
<a class="ui button custom-trigger" data-id="my-item-$ID">Click here</a>
then inside modal container add an "id" tag, for example:
<div id="modal-my-item-$ID" class="ui basic modal">
(...)
</div>
Finally in JavaScript file:
$('.custom-trigger').click(function(event){
var triggerItem = $(this).attr('data-id');
$('#modal-' + triggerItem).modal('show');
});
I meet problem with SUI autofocus, so after a modal opens, screen went to the bottom and button placed there was highlighted.
I tweaked original snippet adding SUI settings:
$('.custom-trigger').click(function(event){
var triggerItem = $(this).attr('data-id');
$('.ui.modal')
.modal({
autofocus: false,
observeChanges: true
})
$('#modal-' + triggerItem).modal('show');
});
If someone wants to write "data-id trigger" manually using fields in CMS, then change $ID to $SomeField variable. Then normally add that field in .php file and in Page Controller add something like this:
public function init() {
parent::init();
Requirements::javascriptTemplate($this->ThemeDir().'/js/script.js', array(
'SomeField' => $this->SomeField
));
}
Hope this helps someone.

Listening to click event on ListView

Feels like I'm missing something stupid here, but what's the recommended method to listen to the click event on a listview?
At the moment I've got:
WinJS.Utilities.query(".menuHolder").listen("click", linkClickHandler, false);
And my listview template uses the class 'menuHolder' for it's items:
<div id="menuTemplate"
data-win-control="WinJS.Binding.Template">
<div class="menuHolder">
<!-- menu img -->
<img src="#" data-win-bind="src : pic; alt : title" />
<div class="menuText">
<!-- menu text -->
<h1 data-win-bind="innerText : title"></h1>
<!-- menu desc -->
<h4 data-win-bind="innerText : description"></h4>
</div>
</div>
</div>
I don't seem to hit my breakpoint, in my link handler, or invoke it's function. Any thoughts?
EDIT:
As a follow on question (bearing in mind the item invoked event) is anyone aware of the recommended approach to pass data between a listview and the iteminvoked event, if I say wanted to use the WinJS.Navigator class to move around an application? I'm guessing I need to cast some part of the eventInfo into a suitable object and retrieve information, what part?
Assuming the data you want to "pass" is the data that is bound to the item that was invoked, you can do that in the event arguments that are passed in to the iteminvoked event. One of mine looks like this...
demosLV.oniteminvoked = function(e) {
e.detail.itemPromise.then(function(item) {
var location = format("/pages/{0}/{0}.html", item.data.key);
WinJS.Navigation.navigate(location, item.data);
});
};
So the demosLV is the ListView. I'm setting the oniteminvoked to a function. That function receives "e" as the event args. In the function I access e.detail.itemPromise and hang a .then off of it. Then I access the actual data in the .then using item.data.
Hope that's what you meant. BTW, the format function is one of mine in case you're wondering why it doesn't work for you.
Seems I was being a sausage, I needed to listen for the 'iteminvoked' event on the parent listview id reference, not the child level.
WinJS.Utilities.query("#menu").listen("iteminvoked", linkClickHandler, false);

Select parent element of known element in Selenium

I have a certain element that I can select with Selenium 1.
Unfortunately I need to click the parent element to get the desired behaviour. The element I can easily locate has attribute unselectable, making it dead for clicking. How do I navigate upwards with XPath?
There are a couple of options there. The sample code is in Java, but a port to other languages should be straightforward.
Java:
WebElement myElement = driver.findElement(By.id("myDiv"));
WebElement parent = (WebElement) ((JavascriptExecutor) driver).executeScript(
"return arguments[0].parentNode;", myElement);
XPath:
WebElement myElement = driver.findElement(By.id("myDiv"));
WebElement parent = myElement.findElement(By.xpath("./.."));
Obtaining the driver from the WebElement
Note: As you can see, for the JavaScript version you'll need the driver. If you don't have direct access to it, you can retrieve it from the WebElement using:
WebDriver driver = ((WrapsDriver) myElement).getWrappedDriver();
Little more about XPath axes
Lets say we have below HTML structure:
<div class="third_level_ancestor">
<nav class="second_level_ancestor">
<div class="parent">
<span>Child</span>
</div>
</nav>
</div>
//span/parent::* - returns any element which is direct parent.
In this case output is <div class="parent">
//span/parent::div[#class="parent"] - returns parent element only of exact node type and only if specified predicate is True.
Output: <div class="parent">
//span/ancestor::* - returns all ancestors (including parent).
Output: <div class="parent">, <nav class="second_level_ancestor">, <div class="third_level_ancestor">...
//span/ancestor-or-self::* - returns all ancestors and current element itself.
Output: <span>Child</span>, <div class="parent">, <nav class="second_level_ancestor">, <div class="third_level_ancestor">...
//span/ancestor::div[2] - returns second ancestor (starting from parent) of type div.
Output: <div class="third_level_ancestor">
Let's consider your DOM as
<a>
<!-- some other icons and texts -->
<span>Close</span>
</a>
Now that you need to select parent tag 'a' based on <span> text, then use
driver.findElement(By.xpath("//a[.//span[text()='Close']]"));
Explanation: Select the node based on its child node's value
Take a look at the possible XPath axes, you are probably looking for parent. Depending on how you are finding the first element, you could just adjust the xpath for that.
Alternatively you can try the double-dot syntax, .. which selects the parent of the current node.
This might be useful for someone else:
Using this sample html
<div class="ParentDiv">
<label for="label">labelName</label>
<input type="button" value="elementToSelect">
</div>
<div class="DontSelect">
<label for="animal">pig</label>
<input type="button" value="elementToSelect">
</div>
If for example, I want to select an element in the same section (e.g div) as a label, you can use this
//label[contains(., 'labelName')]/parent::*//input[#value='elementToSelect']
This just means, look for a label (it could anything like a, h2) called labelName. Navigate to the parent of that label (i.e. div class="ParentDiv"). Search within the descendants of that parent to find any child element with the value of elementToSelect. With this, it will not select the second elementToSelect with DontSelect div as parent.
The trick is that you can reduce search areas for an element by navigating to the parent first and then searching descendant of that parent for the element you need.
Other Syntax like following-sibling::h2 can also be used in some cases. This means the sibling following element h2. This will work for elements at the same level, having the same parent.
We can select the parent tag with the help of Selenium as follows:
driver.findElement(By.xpath("//table[#id='abc']//div/nobr[.='abc']/../.."));
this will help you to find the grandparent of the known Element. Just Remove one (/..) to find the immediate Parent Element.
Like:
driver.findElement(By.xpath("//table[#id='abc']//div/nobr[.='abc']/..));
There are some other ways to implement this, but it worked fine for me.
You can do this by using /parent::node() in the xpath. Simply append /parent::node() to the child elements xpath.
For example:
Let xpath of child element is childElementXpath.
Then xpath of its immediate ancestor would be childElementXpath/parent::node().
Xpath of its next ancestor would be childElementXpath/parent::node()/parent::node()
and so on..
Also, you can navigate to an ancestor of an element using
'childElementXpath/ancestor::*[#attr="attr_value"]'. This would be useful when you have a known child element which is unique but has a parent element which cannot be uniquely identified.
Have once way you don't need to execute script and you still get the parent element:
// identify element
WebElement ele =driver.findElement(By.xpath("//*[#id=\"ext-gen6\"]/div[5]/"));
//identify parent element with ./.. expression in xpath
WebElement parent = ele.findElement(By.xpath("./.."));
The key word here is xpath "./.."

Add & Delete HTML DIV Dynamically

I have a problem to add and delete html div dynamically. Below is the simplified code that I am working on:
<input type="button" value="+ Add More Division" name="add_div" id="add_div">
<div id="div_1">
<input type="button" value="+ Delete Div A" name="del_a" id="del_a">
</div>
<div id="div_2">
<input type="button" value="+ Delete Div B" name="del_b" id="del_b">
</div>
<div id="div_3">
<input type="button" value="+ Delete Div C" name="del_c" id="del_c">
</div>
This is the situation, div_1, div_2 and div_3 can be added and removed dynamically. div_1 will always be associated with button id del_a, div_2 with del_b and div_3 with del_c. It is only 3 divs maximum can be added.
I need jquery that can add and remove the div and reuse div_1, div_2 and div_3 along with the associated button ids.
For example, if the user delete the div_2, and the user want to add more division (which is only 1 more can be added), the jquery will try to find the existing divs, and somehow remember it. Since div_2 is not exist, div_2 will be added, does not matter the order. div_3 can go first before or after the other divs.
I just want to give freedom for the user to edit the form, since this massive form. I do not know how to do this in jQuery using .each().
Thanks for your help.
Edited:
By default, only div_1 is exist on the form. The user can have the freedom to add another 2 divs to add more divs. The user is also have the capability to delete div_1 when either div_2 or div_3 exist. One div must be exist.
you can use detach() function....it will simply remove the div and if you want to add it again have, a reference of that div and add it again..something like this
suppose, user deleted the first div like this...
var div
$('#del_a').click(function(){
div= $('#div_1').detach();
});
...
and when user click on add button you can do something like this...
$('#add_div').click(function(){
$(div).appendTo('where you want to append')
});
i think this might help you..
If you want to delete div dynamically den use it:
$('#mydiv').empty();
Add /Remove dynamically HTML element with jQuery plz see below
Add /Remove dynamically HTML element with jQuery
You can have three variables acting as flags for the divs present. When the add div button is clicked, check for the first flag which is not set, add that div and set the flag for that particular div. If all three flags are set, then disable the add more divs button. Hope this helps.
Its working fine (Plz optimized this code) :)
<script type="text/javascript" >
$(document).ready(function(){
$('#add_div').click(function(){
if(($('#del_a').is(":hidden"))){$("#del_a").show(); return; }
if(($('#del_b').is(":hidden"))){$("#del_b").show(); return; }
if(($('#del_c').is(":hidden"))){$("#del_c").show(); return; }
});
$('#del_a').click(function(){
if($('#del_b').is(":hidden") && $('#del_c').is(":hidden") )
return;
$('#del_a').hide();
});
$('#del_b').click(function(){
if($('#del_a').is(":hidden") && $('#del_c').is(":hidden") )
return;
$('#del_b').hide();
});
$('#del_c').click(function(){
if($('#del_a').is(":hidden") && $('#del_b').is(":hidden") )
return;
$('#del_c').hide();
});
});