Documented-Style-Sheets / DSS-Recorder

:movie_camera: A broswer extension to record and save interactivity to be played back
12 stars 2 forks source link

Thoughts on creating unique selectors #3

Open darcyclarke opened 9 years ago

darcyclarke commented 9 years ago

@richgilbank you mentioned you may have something in the works for this. Would love a peak/insight. I know right now we store the HTMLElement Object, which isn't ideal.

Eventually, we'll need to do something like JSON.stringify( this.dssEvents ) and storing element objects wouldn't be great if we want the output to be pretty-printed for manual manipulation/storage. I think the best case scenario is to be able to store a selector string.

One strategy could be:

  1. Check for an id
  2. Fallback: Generate a selector for the element, based on its attributes and tagname, then find it's position relative to any other elements matching that selector (thus, creating a unique selector string)

I may mock this out if I have time but let me know your thoughts.

darcyclarke commented 9 years ago

Okay... it's a bit rough around the edges but it seems to work (demo: http://jsfiddle.net/darcyclarke/gn3nwanc/15/)

// Generate A Unique Selector
var generateUniqeSelector = function ( el ) {

    var index;
    var siblings;
    var selector = '';
    var scopedSelector = '';
    var body = document.querySelector( 'body' );
    var tagName = el.tagName.toLowerCase();
    var parent = el.parentNode;    
    var attrs = (function( attrs ) {
        if ( attrs.length <= 0 ) {
            return '';
        }
        var str = '';
        attrs.forEach( function( attr ) { 
            str += '[' + attr.nodeName + '="' + attr.textContent + '"]'; 
        });
        return str;
    })( Array.prototype.slice.call( el.attributes ) );

    // Return if we have a unique selector
    if ( el.id ) {
       return '#' + el.id; 
    }

    // Return if we are dealing with body or html elements 
    if ( tagName === 'html' || tagName === 'body' ) {
        return tagName;   
    }

    // Return if tagname + attrs generates unique selector
    selector = tagName + attrs;
    siblings = document.querySelectorAll( selector ); 
    if ( siblings.length === 1 ) {
        return selector;        
    }

    // Return a truly unique selector using a scoped selector and index
    index = Array.prototype.indexOf.call( parent.children, el );
    while( parent != body ) {
        parent = parent.parentNode();   
        scopedSelector = parent.tagName.toLowerCase() + scopedSelector + ' ';
    }
    return 'body ' + scopedSelector + '*:nth-child(' + index + ')'; 

};

Probably needs more testing with nested elements to make sure the walker actually does its job to generate the right scoped selector... but yea... it seems to do the trick

darcyclarke commented 9 years ago

@richgilbank does this look good/work?