@@ -632,6 +632,63 @@ lib.isD3Selection = function(obj) {
632632 return obj && ( typeof obj . classed === 'function' ) ;
633633} ;
634634
635+ /**
636+ * Append element to DOM only if not present.
637+ *
638+ * @param {d3 selection } parent : parent selection of the element in question
639+ * @param {string } nodeType : node type of element to append
640+ * @param {string } className : class name of element in question
641+ * @param {fn } enterFn (optional) : optional fn applied to entering elements only
642+ * @return {d3 selection } selection of new layer
643+ *
644+ * Previously, we were using the following pattern:
645+ *
646+ * ```
647+ * var sel = parent.selectAll('.' + className)
648+ * .data([0]);
649+ *
650+ * sel.enter().append(nodeType)
651+ * .classed(className, true);
652+ *
653+ * return sel;
654+ * ```
655+ *
656+ * in numerous places in our codebase to achieve the same behavior.
657+ *
658+ * The logic below performs much better, mostly as we are using
659+ * `.select` instead `.selectAll` that is `querySelector` instead of
660+ * `querySelectorAll`.
661+ *
662+ */
663+ lib . ensureSingle = function ( parent , nodeType , className , enterFn ) {
664+ var sel = parent . select ( nodeType + ( className ? '.' + className : '' ) ) ;
665+ if ( sel . size ( ) ) return sel ;
666+
667+ var layer = parent . append ( nodeType ) . classed ( className , true ) ;
668+ if ( enterFn ) layer . call ( enterFn ) ;
669+
670+ return layer ;
671+ } ;
672+
673+ /**
674+ * Same as Lib.ensureSingle, but using id as selector.
675+ * This version is mostly used for clipPath nodes.
676+ *
677+ * @param {d3 selection } parent : parent selection of the element in question
678+ * @param {string } nodeType : node type of element to append
679+ * @param {string } id : id of element in question
680+ * @param {fn } enterFn (optional) : optional fn applied to entering elements only
681+ * @return {d3 selection } selection of new layer
682+ */
683+ lib . ensureSingleById = function ( parent , nodeType , id , enterFn ) {
684+ var sel = parent . select ( nodeType + '#' + id ) ;
685+ if ( sel . size ( ) ) return sel ;
686+
687+ var layer = parent . append ( nodeType ) . attr ( 'id' , id ) ;
688+ if ( enterFn ) layer . call ( enterFn ) ;
689+
690+ return layer ;
691+ } ;
635692
636693/**
637694 * Converts a string path to an object.
0 commit comments