Javascript/Jquery stable nested sibling - javascript

On an HTML page which repeats a nested structure like
<div>
<div class="ugga">
<button class="theButton">
</div>
</div>
several times, with one ".theButton" also having class "active", I would like to use jquery to find the button after the active button.
$(".theButton .active").parents(".ugga").parent().next().find(".theButton")
would roughly do the trick. However, this is still under development, so that I am not sure that the nesting level div/div/button as well as the parent element with ".ugga" will be stable. So whenever there is a structure change on the HTML side, I would have to change the above jquery-magic accordingly.
What is stable is that there will be a list of ".theButton" elements at some nesting level and all on the same nesting level.
Is there a simple way in Jquery to find the next button after the active one even if the structure is changed to just div/button or to form/div/div/button and the ".ugga" I rely on currently disappears? Something like
$(".theButton active").nextOnSameLevel(".theButton")

There's no short and simple solution I know of, which would let you to do that.
The most convenient way would be to have the HTML ready before setting up javascript for DOM manipulation. Even thinking of project updates I would personally spent that little while to change a small part of js.
However, if that's needed for some reason, then I would probably loop through the elements, to find the one I need, eg.:
var found = false;
$(".theButton").each(function(){
if(found){
// do something with $(this) ...
return false;
}
if($(this).hasClass('active')){
found = true;
}
});
JSFiddle
And yet another, oneliner solution:
$(".theButton").eq(($.inArray($(".theButton.active")[0], $(".theButton").toArray()))+1);
JSFiddle

This is for looking at all the other elements having the same parent as your element of interest.
$(".theButton active").siblings(".theButton");
This will return all the elements having theButton before and after your active button elements but if you're looking specifically for the element after active, use next() with a selector like this
$(".theButton active").next(".theButton");

Related

ScrollIntoView in React without refs?

I have a big list of items / divs that I need to create programmatically, and I need to implement scrollIntoView buttons for each them. I know how to do it with refs.
Whether there is an alternative to refs that might be more performant?
I believe something like this will work:
onScrollClick(ev) {
ev.target.scrollIntoView();
}
render() {
return (
... <button onClick={this.onScrollClick}>Scroll This Element Into View</button>
)
}
That assumes you want to scroll the button itself into view. If that's not what you want, you'll have to be specific.
[edit] if it's not the element itself, but one of the elements parents, you can also find a parent with javascript apis, element.parentElement -- you can use that as many times as you need to find the relevant element.

jQuery Select One Element From Class name

I am in a position where I need to use the .slideToggle() function in jQuery, on a regular JavaScript determined element.
I can use this code:
var feedback = document.getElementsByClassName('feedback');
and then a bit later on in a function:
feedback[index].style.display = 'block';
However, what I want to do is use the slideToggle('fast') function on feedback[index], so instead of so brutally changing its display to block, I get a nice jQuery-esque transition.
Obviously this code won't work:
feedback[index].slideToggle('fast');
However this will:
$('.feedback').slideToggle('fast');
but I can't choose which feedback by index to run the slideToggle() function on, it just does them all, which makes sense.
If I could get some code that effectively does this:
$('.feedback')[index].slideToggle('fast');
That would be perfect. I like the fact that I can stick a class on something and iterate through the list of items that appear in .getElementsByClassName('classname'), so I don't have to stick an ID on everything of the same class, and it would be nice if I could choose which $('.feedback') element I am using in the list of all elements returned by this but I cannot figure out how that would work. If I can somehow choose by index which items in a list by class, to run jQuery commands on it would make this a lot simpler as I do not want to stick an ID on each and every item that has the class of feedback.
Thanks a lot.
Try this : You can use eq() to select element with specific index.
$('.feedback:eq('+index+')').slideToggle('fast');
You can use JQuery like this:
$(feedback[index]).slideToggle('fast');
Here you can see demo

Protractor: How to locate a sibling of a given element?

<div>
<a href='...'>LINK</a>
<img class='image' />
</div>
<div>
...
</div>
I want to get a protractor element for the img tag with image class. I already know the link text 'LINK'. In other words, "How do I locate a sibling of a given element?".
The first line of the code could look like this:
browser.findElement(by.linkText('LINK'))
Any ideas?
Thanks & Cheers
Thanks for the inspiration. Here's my solution, not the one I was hoping for, but it works:
element(by.css('???')).element(by.xpath('..')).element(by.css('???')).click();
The chaining and the by.xpath, which allows to get back to the parent are the keys of the solution.
This is what I actually implement on a Page Object:
this.widgets['select-status'] = this.ids['select-status']
.element(by.xpath('following-sibling::div[1]'));
this.widgets['select-status.dropdown'] = element(by.css('.btn-group.bootstrap-select.open'));
The page is based on Bootstrap along with Bootstrap Select. Anyways, we traverse the DOM along the following-sibling axis. Refer to XPATH specification for yourself.
Using Xpath selectors is not a better choice as it slows down the element finding mechanism.
I have designed a plugin to address this specific issues: protractor-css-booster
The plugin provides some handly locators to find out siblings in a better way and most importantly with CSS selector.
using this plugin, you can directly use:
var elem = await element(by.cssContainingText('a','link text')).nextSibling();
elem.click(); //proceed with your work
or, use this as by-locator
var elem = element(by.cssContainingText('a','link text')).element(by.followingSibling('img'));
You can always checkout the other handy methods available here...
Now, you can find web elements such as:
Finding Grand Parent Element
Finding Parent Element
Finding Next Sibling
Finding Previous Sibling
Finding any Following Sibling
Finding First Child Element
Finding Last Child Element
And, guess what, everything you can find using 💥 CSS Selectors 💥
Hope, it will help you...

Show context-menus only when certain elements are clicked

I am kinda stuck with something and I need your help.
I am trying to show context-menus only when a user right-clicks on a certain elements in the page.
I thought I solve this problem by using getElementByClassName(...) and adding an onClick listener to each one of the elements, and when the user clicks on any of them I will then create the context-menus. And then remove the content menu later when everything is done.
Problem is that I don't have the full class names of those elements, all I know that they start with "story".
I am not sure how to go about doing this. Is there a way to use regex and getting all elements with a class name of story? Or is that not possible.
Thanks in advance,
There's this library that allows for regex selectors.
<div class="story-blabla"></div>
$("div:regex(class, story.*)")
However, you may not want to implement a full library. There's another solution:
$('div').filter(function() {
return this.class.match(/story.*/);
})
This will return the objects you want.
You can do this using attribute starts with selector
document.querySelectorAll("[class^=story]")

HTML5,draggable and contentEditable not working together

When a draggable attribute is enabled on a parent element(<li>) I cant make contenteditable work on its child element (<a>).
The focus goes on to child element (<a>),but I cant edit it at all.
Please check this sample
http://jsfiddle.net/pavank/4rdpV/11/
EDIT: I can edit content when I disable draggable on <li>
I came across the same problem today, and found a solution [using jQuery]
$('body').delegate('[contenteditable=true]','focus',function(){
$(this).parents('[draggable=true]')
.attr('data-draggableDisabled',1)
.removeAttr('draggable');
$(this).blur(function(){
$(this).parents('[data-draggableDisabled="1"]')
.attr('draggable','true')
.removeAttr('data-draggableDisabled');
});
});
$('body') can be replaced by anything more specific.
If new contenteditable elements are not added in the runtime, one can use bind instead of delegate.
It makes sense that the draggable and contenteditable properties would collide. contenteditable elements, like any text field, will focus on mousedown (not click). draggable elements operate based on mousemove, but so does selecting text in a contenteditable element, so how would the browser determine whether you are trying to drag the element or select text? Since the properties can't coexist on the same element, it appears that you need a javascript solution.
Try adding these two attributes to your anchor tag:
onfocus="this.parentNode.draggable = false;"
onblur="this.parentNode.draggable = true;"
That works for me if I add it to the <a> tags in your jsFiddle. You could also use jQuery if it's more complicated than getting the parentNode.
Note: This is a workaround since I believe the inability for these two functionalities to work together resides in the HTML spec itself (i.e. the not working together thing is intentional since the browser can't determine whether you want to focus or drag on the mousedown event)
I noticed you explicitly set 'no libraries', so I will provide a raw javascript/HTML5 answer
http://jsfiddle.net/4rdpV/26/
This was my crack at it.
First of all, it might be better to include the data in one single localStorage item, rather than scatter it.
storage={
'1.text':'test 1',
'2.text':'test 2'
}
if(localStorage['test']){
storage=JSON.parse(localStorage['test'])
}
this creates that ability, using JSON to convert between object and string. Objects can indeed be nested
I also added (edit) links next to the items, when clicked, these links will transform the items into input elements, so you can edit the text. After hitting enter, it transforms it back and saves the data. At the same time, the list items remain draggable.
After saving, hit F12 in chrome, find the console, and look in the localStorage object, you will see all the data was saved in localStorage['test'] as an Object using JSON.stringify()
I tried my best to design this to be scaleable, and I think I succeeded well enough; you just need to replace the HTML with a container and use a javascript for loop to write out several items, using the iterator of your choice to fill the parameter for edit(). For example:
Say you changed storage to hold "paradigms" of lists, and you have one called "shopping list". And say the storage object looks something like this:
{
"shopping list":{
1:"Milk",
2:"Eggs",
3:"Bread"
}
}
This could render that list out:
for(i in storage['shopping list']){
_item = storage['shopping list'][i];
container.innerHTML+='<li draggable=true><a id="item'+i+'">'+_item+'</a> (edit)</li>'
}
Of course, if you were to edit the structure of the storage object, you would need to edit the functions as well.
The output would look something like this:
Milk (edit)
Eggs (edit)
Bread (edit)
Don't worry about the input elements if that worries you; CSS can easily fix it to look like it didn't just change.
If you don't want the (edit) links to be visible, for example, you can do this in CSS:
a[href="#"]{
display:none;
}
li[draggable="true"]:hover a[href="#"]{
display:inline;
}
Now the edit links will only appear when you hover the mouse over the list item, like this version:
http://jsfiddle.net/4rdpV/27/
I hope this answer helped.
Using html5sortable and newer JQuery events (delegate is deprecated, answer 3 years after initial question), bug still affects Chrome 37. Contenteditable spans and html5sortable seem to play nice in other browsers. I know this is only partially relevant, just keeping documentation on changes I've noticed.
$(document).on('focus', 'li span[contenteditable]', function() {
$(this).parent().parent().sortable('destroy'); // removes sortable from the whole parent UL
});
$(document).on('blur', 'li span[contenteditable]', function() {
$(this).parent().parent().sortable({ connectWith: '.sortable' }); // re-adds sortable to the parent UL
});

Categories